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/// Top-level error type for all ez-ffmpeg operations.
18///
19/// Most variants wrap a stage-specific error enum (opening inputs and
20/// outputs, demuxing, decoding, filtering, encoding, muxing, ...), so
21/// callers can match on the pipeline stage first and inspect the typed
22/// cause when they need to.
23#[derive(thiserror::Error, Debug)]
24#[non_exhaustive]
25pub enum Error {
26 /// Returned when an operation requires a scheduler that has been
27 /// started, but it has not been.
28 #[error("Scheduler is not started")]
29 NotStarted,
30
31 /// A URL or path string could not be converted into a C string for
32 /// FFmpeg (see [`UrlError`]).
33 #[error("URL error: {0}")]
34 Url(#[from] UrlError),
35
36 /// Opening an input file, stream, device, or custom input source failed.
37 #[error("Open input stream error: {0}")]
38 OpenInputStream(#[from] OpenInputError),
39
40 /// Probing stream information from an opened input failed.
41 #[error("Find stream info error: {0}")]
42 FindStream(#[from] FindStreamError),
43
44 /// Resolving a decoder failed (see [`DecoderError`]).
45 #[error("Decoder error: {0}")]
46 Decoder(#[from] DecoderError),
47
48 /// Parsing a filtergraph description failed.
49 #[error("Filter graph parse error: {0}")]
50 FilterGraphParse(#[from] FilterGraphParseError),
51
52 /// Returned when a filtergraph link label could not be converted to a
53 /// UTF-8 string.
54 #[error("Filter description converted to utf8 string error")]
55 FilterDescUtf8,
56
57 /// Returned when a filter name could not be converted to a UTF-8 string.
58 #[error("Filter name converted to utf8 string error")]
59 FilterNameUtf8,
60
61 /// Returned when a filtergraph declares zero outputs, which is not
62 /// supported.
63 #[error("A filtergraph has zero outputs, this is not supported")]
64 FilterZeroOutputs,
65
66 /// Returned when a filtergraph declares zero inputs, which is not
67 /// supported.
68 #[error("A filtergraph has zero inputs, this is not supported")]
69 FilterZeroInputs,
70
71 /// Returned when a numeric field — such as a file index in a stream
72 /// specifier or link label — could not be parsed as an integer.
73 #[error("Input is not a valid number")]
74 ParseInteger,
75
76 /// Allocating an output format context failed.
77 #[error("Alloc output context error: {0}")]
78 AllocOutputContext(#[from] AllocOutputContextError),
79
80 /// Opening or configuring an output failed.
81 #[error("Open output error: {0}")]
82 OpenOutput(#[from] OpenOutputError),
83
84 /// Returned when an output URL is identical to one of the input URLs;
85 /// the payload is the offending path. In-place editing is not supported.
86 #[error("Output file '{0}' is the same as an input file")]
87 FileSameAsInput(String),
88
89 /// Enumerating capture devices failed.
90 #[error("Find devices error: {0}")]
91 FindDevices(#[from] FindDevicesError),
92
93 /// Allocating an `AVFrame` failed.
94 #[error("Alloc frame error: {0}")]
95 AllocFrame(#[from] AllocFrameError),
96
97 /// Allocating an `AVPacket` failed.
98 #[error("Alloc packet error: {0}")]
99 AllocPacket(#[from] AllocPacketError),
100
101 /// Making a frame's data buffers writable failed (see
102 /// [`FrameWritableError`]).
103 #[error("Frame writable error: {0}")]
104 FrameWritable(#[from] FrameWritableError),
105
106 // ---- Muxing ----
107 /// A muxing operation failed while writing the output container.
108 #[error("Muxing operation failed {0}")]
109 Muxing(#[from] MuxingOperationError),
110
111 // ---- Open Encoder ----
112 /// Opening or configuring an encoder failed.
113 #[error("Open encoder operation failed {0}")]
114 OpenEncoder(#[from] OpenEncoderOperationError),
115
116 // ---- Encoding ----
117 /// An encoding operation failed.
118 #[error("Encoding operation failed {0}")]
119 Encoding(#[from] EncodingOperationError),
120
121 // ---- FilterGraph ----
122 /// A filtergraph runtime operation failed.
123 #[error("Filter graph operation failed {0}")]
124 FilterGraph(#[from] FilterGraphOperationError),
125
126 // ---- Open Decoder ----
127 /// Opening or configuring a decoder failed.
128 #[error("Open decoder operation failed {0}")]
129 OpenDecoder(#[from] OpenDecoderOperationError),
130
131 // ---- Decoding ----
132 /// A decoding operation failed.
133 #[error("Decoding operation failed {0}")]
134 Decoding(#[from] DecodingOperationError),
135
136 // ---- Demuxing ----
137 /// A demuxing operation failed.
138 #[error("Demuxing operation failed {0}")]
139 Demuxing(#[from] DemuxingOperationError),
140
141 // ---- Packet Scanner ----
142 /// A packet-scanning operation failed (see [`PacketScannerError`]).
143 #[error("Packet scanner error: {0}")]
144 PacketScanner(#[from] PacketScannerError),
145
146 // ---- Frame Filter ----
147 /// A frame filter failed to initialize; carries the error returned by
148 /// the filter's `init`.
149 #[error("Frame filter init failed: {0}")]
150 FrameFilterInit(Box<dyn std::error::Error + Send + Sync>),
151
152 /// A frame filter failed while processing a frame; carries the error
153 /// returned by the filter's `filter_frame`.
154 #[error("Frame filter process failed: {0}")]
155 FrameFilterProcess(Box<dyn std::error::Error + Send + Sync>),
156
157 /// A frame filter failed while generating a frame; carries the error
158 /// returned by the filter's `request_frame`.
159 #[error("Frame filter request failed: {0}")]
160 FrameFilterRequest(Box<dyn std::error::Error + Send + Sync>),
161
162 /// Returned while building a frame pipeline when no stream of the
163 /// required media type exists at the named pipeline end; fields are the
164 /// pipeline end (input/output) and the media type.
165 #[error("No {0} stream of the type:{1} were found while build frame pipeline")]
166 FrameFilterTypeNoMatched(String, String),
167
168 /// Returned while building a frame pipeline when no stream at the named
169 /// pipeline end matches both the requested stream index and media type;
170 /// fields are the pipeline end, the stream index, and the media type.
171 #[error("{0} stream:{1} of the type:{2} were mismatched while build frame pipeline")]
172 FrameFilterStreamTypeNoMatched(String, usize, String),
173
174 /// Returned when a frame pipeline tries to deliver a frame to a
175 /// destination that has already finished.
176 #[error("Frame filter pipeline destination already finished")]
177 FrameFilterDstFinished,
178
179 /// Returned when a frame pipeline fails to duplicate a frame required
180 /// for an additional destination.
181 #[error("Frame filter pipeline failed to duplicate a frame for an additional destination")]
182 FrameFilterFrameDuplicateFailed,
183
184 /// Returned when spawning a frame pipeline's worker thread fails, so
185 /// the pipeline never ran.
186 #[error("Frame filter pipeline thread exited")]
187 FrameFilterThreadExited,
188
189 /// A worker thread panicked; the payload is the worker's thread name.
190 /// Output may be incomplete.
191 #[error("Worker thread '{0}' panicked; output may be incomplete")]
192 WorkerPanicked(String),
193
194 /// Recorded as the scheduler result when `start()` fails after some
195 /// worker threads were already launched. `start()` itself returns the
196 /// actual init error to its caller; this recorded value is what
197 /// concurrent observers (packet-sink terminal callbacks) report, so a
198 /// sink can never mistake a torn-down startup for a settled-Ok job.
199 #[error("Scheduler start failed; the job was torn down during startup")]
200 StartFailed,
201
202 /// Returned when publishing to the embedded RTMP server with a stream
203 /// key that is already in use; the payload is the key.
204 #[cfg(feature = "rtmp")]
205 #[error("Rtmp stream already exists with key: {0}")]
206 RtmpStreamAlreadyExists(String),
207
208 /// Returned when a stream could not be created on the embedded RTMP
209 /// server, typically because the server has stopped.
210 #[cfg(feature = "rtmp")]
211 #[error("Rtmp create stream failed. Check whether the server is stopped.")]
212 RtmpCreateStream,
213
214 /// Returned when too many streams are waiting to be registered on the
215 /// embedded RTMP server.
216 #[cfg(feature = "rtmp")]
217 #[error("Rtmp registration queue is full: too many streams are waiting to be registered")]
218 RtmpRegistrationQueueFull,
219
220 /// Returned when the embedded RTMP server's thread has exited.
221 #[cfg(feature = "rtmp")]
222 #[error("Rtmp server thread exited")]
223 RtmpThreadExited,
224
225 /// Returned when the embedded RTMP server is no longer consuming a
226 /// published stream.
227 #[cfg(feature = "rtmp")]
228 #[error("Rtmp stream closed: the server is no longer consuming this stream")]
229 RtmpStreamClosed,
230
231 /// Returned when starting an embedded RTMP server that was already
232 /// started; clones of one server share a single lifecycle that can be
233 /// started only once.
234 #[cfg(feature = "rtmp")]
235 #[error("Rtmp server already started: clones of one server share a single lifecycle, which can be started only once")]
236 RtmpServerAlreadyStarted,
237
238 /// A subtitle processing operation failed.
239 #[cfg(feature = "subtitle")]
240 #[error("Subtitle error: {0}")]
241 Subtitle(#[from] crate::subtitle::SubtitleError),
242
243 /// A wgpu GPU filter operation failed.
244 #[cfg(feature = "wgpu")]
245 #[error("Wgpu filter error: {0}")]
246 WgpuFilter(#[from] crate::wgpu_filter::WgpuFilterError),
247
248 // The allow covers the deprecation that OpenGLFilterError inherits from
249 // the deprecated `opengl` module; the variant must still carry the type.
250 // From is hand-written below the enum (a derived #[from] would re-name
251 // the type in generated code that no #[allow] on the variant reaches).
252 /// An OpenGL filter operation failed (deprecated `opengl` feature).
253 #[cfg(feature = "opengl")]
254 #[allow(deprecated)]
255 #[error("OpenGL filter error: {0}")]
256 OpenGLFilter(#[source] OpenGLFilterError),
257
258 /// An I/O error from the standard library.
259 #[error("IO error:{0}")]
260 IO(#[from] io::Error),
261
262 /// Internal end-of-stream marker passed between pipeline stages; a
263 /// normal end of input is consumed internally rather than reported as
264 /// a job failure.
265 #[error("EOF")]
266 EOF,
267 /// Internal control-flow marker instructing pipeline stages to shut
268 /// down; normally consumed internally.
269 #[error("Exit")]
270 Exit,
271 /// Internal invariant violation that should never occur; indicates a
272 /// bug in this crate rather than a problem with user input.
273 #[error("Bug")]
274 Bug,
275
276 /// Returned when a recipe or analysis option is invalid (out of range,
277 /// malformed, or inconsistent); the payload describes the problem.
278 #[error("Invalid recipe argument: {0}")]
279 InvalidRecipeArg(String),
280
281 /// A container-info query was called with an out-of-range index.
282 #[error("Container info error: {0}")]
283 ContainerInfo(#[from] ContainerInfoError),
284
285 /// A frame-export operation failed.
286 #[error("Frame export error: {0}")]
287 FrameExport(#[from] crate::core::frame_export::FrameExportError),
288
289 /// Building or opening a video writer failed.
290 #[error("Video writer error: {0}")]
291 Writer(#[from] crate::core::writer::WriterError),
292
293 /// Pushing a frame into a video writer failed.
294 #[error("Video writer push error: {0}")]
295 Push(#[from] crate::core::writer::PushError),
296
297 /// Returned when a frame-source input's worker thread failed to start.
298 #[error("Frame source thread failed to start")]
299 FrameSourceThreadExited,
300
301 /// A packet-sink output failed (see [`PacketSinkError`]).
302 #[error("Packet sink error: {0}")]
303 PacketSink(#[from] PacketSinkError),
304
305 /// CLI-compat pipelines only: a `-vf` command was lowered onto an input
306 /// whose OPENED demuxer does not carry exactly one video stream. The
307 /// check runs on the demuxer instance the pipeline actually executes
308 /// with (no separate probe opening, no TOCTOU window). The facade maps
309 /// this to its public `AmbiguousFilterSource` diagnostic.
310 #[cfg(feature = "cli")]
311 #[error("the per-output video filter requires exactly one video stream in the input; the opened input has {video_streams}")]
312 AmbiguousVideoSource {
313 /// Number of video streams carried by the opened input.
314 video_streams: usize,
315 },
316
317 /// Strict AVOption handling (CLI-compat pipelines): an option the caller
318 /// supplied was not consumed by the component it targeted. The default
319 /// builder path only WARNS about such leftovers; pipelines built through
320 /// the `cli` feature's entry points fail instead, mirroring fftools'
321 /// `check_avoptions` abort. Only exists with the `cli` feature — the
322 /// feature-off API surface is unchanged.
323 #[cfg(feature = "cli")]
324 #[error("option '{option}' was not consumed by {site}; CLI-compat strict mode treats leftover AVOptions as errors")]
325 UnconsumedCliOption {
326 /// Human-readable description of the component that should have
327 /// consumed the option (e.g. "the muxer of output 0").
328 site: String,
329 /// The option key that was left unconsumed.
330 option: String,
331 },
332}
333
334// `Error` rides in every hot-path `Result` — the per-frame encoder and filter
335// calls return `Result<(), Error>` / `Result<bool, Error>` — so its size is a
336// layout contract, not an implementation detail: one oversized payload grows
337// every such `Result` crate-wide. 64 bytes is the long-standing layout; keep
338// new payloads inside it (use static labels for fixed vocabulary, or box a
339// genuinely large variant). A const assertion rather than a #[test] so that
340// merely compiling the crate enforces the bound for whichever feature-gated
341// variants that build carries — including feature combinations whose tests
342// are compiled but never run.
343const _: () = assert!(
344 std::mem::size_of::<Error>() <= 64,
345 "Error grew past its 64-byte layout: shrink the new payload (static labels) or box the variant"
346);
347
348/// Builder/open-time validation errors for [`crate::VideoWriter`]. Exported here
349/// (not from the crate root) to mirror the existing `OpenInputError` /
350/// `OpenOutputError` organization; the root surface stays the settled writer
351/// types (the writer itself, its builder, and the push error pair).
352pub use crate::core::writer::WriterError;
353
354// Hand-written counterpart of the #[from] the sibling variants derive: the
355// error type inherits deprecation from the deprecated `opengl` module, so
356// the conversion is spelled out where the lint can be silenced.
357#[cfg(feature = "opengl")]
358#[allow(deprecated)]
359impl From<OpenGLFilterError> for Error {
360 fn from(err: OpenGLFilterError) -> Self {
361 Error::OpenGLFilter(err)
362 }
363}
364
365/// Errors from the `container_info` queries where the caller asked for an index
366/// outside the container's range. These are caller/argument errors — a bad index
367/// into an otherwise valid container — kept distinct from an open/probe failure
368/// (`OpenInputError` / `FindStreamError`) so retry logic, telemetry, and user
369/// messages can tell "you asked for chapter 5 of a 3-chapter file" apart from
370/// "the file is corrupt or unreadable". Each variant carries the offending
371/// `index` and the container's actual `count`.
372#[derive(thiserror::Error, Debug)]
373#[non_exhaustive]
374pub enum ContainerInfoError {
375 /// Returned when the requested chapter index exceeds the number of
376 /// chapters in the container.
377 #[error("chapter index {index} out of range: the container has {count} chapter(s)")]
378 ChapterIndexOutOfRange {
379 /// The chapter index that was requested.
380 index: usize,
381 /// Number of chapters the container actually has.
382 count: usize,
383 },
384
385 /// Returned when the requested stream index exceeds the number of
386 /// streams in the container.
387 #[error("stream index {index} out of range: the container has {count} stream(s)")]
388 StreamIndexOutOfRange {
389 /// The stream index that was requested.
390 index: usize,
391 /// Number of streams the container actually has.
392 count: usize,
393 },
394}
395
396/// Error type for RTMP streaming operations using StreamBuilder
397#[cfg(feature = "rtmp")]
398#[derive(thiserror::Error, Debug)]
399#[non_exhaustive]
400pub enum StreamError {
401 /// Returned when a required builder parameter was never set; the
402 /// payload is the parameter name (e.g. "address", "stream_key").
403 #[error("missing required parameter: {0}")]
404 MissingParameter(&'static str),
405
406 /// Returned when the configured input path does not point to an
407 /// existing file.
408 #[error("input path is not a valid file: {path}")]
409 InputNotFound {
410 /// The input path that failed validation.
411 path: std::path::PathBuf,
412 },
413
414 /// An underlying ez-ffmpeg error raised while building or running the
415 /// stream.
416 #[error("ffmpeg error: {0}")]
417 Ffmpeg(#[from] crate::error::Error),
418}
419
420impl PartialEq for Error {
421 /// Structural equality for payload-less variants only. Variants carrying
422 /// an inner error compare unequal even to themselves — use matches! on
423 /// the variant when that is what you mean.
424 fn eq(&self, other: &Self) -> bool {
425 use Error::*;
426 match (self, other) {
427 (NotStarted, NotStarted)
428 | (FilterDescUtf8, FilterDescUtf8)
429 | (FilterNameUtf8, FilterNameUtf8)
430 | (FilterZeroOutputs, FilterZeroOutputs)
431 | (FilterZeroInputs, FilterZeroInputs)
432 | (ParseInteger, ParseInteger)
433 | (FrameFilterDstFinished, FrameFilterDstFinished)
434 | (FrameFilterFrameDuplicateFailed, FrameFilterFrameDuplicateFailed)
435 | (FrameFilterThreadExited, FrameFilterThreadExited)
436 | (FrameSourceThreadExited, FrameSourceThreadExited)
437 | (EOF, EOF)
438 | (Exit, Exit)
439 | (Bug, Bug) => true,
440 #[cfg(feature = "rtmp")]
441 (RtmpCreateStream, RtmpCreateStream)
442 | (RtmpRegistrationQueueFull, RtmpRegistrationQueueFull)
443 | (RtmpThreadExited, RtmpThreadExited)
444 | (RtmpStreamClosed, RtmpStreamClosed)
445 | (RtmpServerAlreadyStarted, RtmpServerAlreadyStarted) => true,
446 _ => false,
447 }
448 }
449}
450
451// No Eq impl: variants carrying payloads are not equal to themselves, so
452// the relation is not reflexive and claiming Eq would be a lie.
453
454/// Errors from the demuxer stage while reading packets from an input.
455/// Variants carrying a [`DemuxingError`] embed the mapped FFmpeg error.
456#[derive(thiserror::Error, Debug)]
457#[non_exhaustive]
458pub enum DemuxingOperationError {
459 /// Returned when reading the next packet from the input fails
460 /// (`av_read_frame`).
461 #[error("while reading frame: {0}")]
462 ReadFrameError(DemuxingError),
463
464 /// Returned when creating an additional reference to a demuxed packet
465 /// fails (`av_packet_ref`).
466 #[error("while referencing packet: {0}")]
467 PacketRefError(DemuxingError),
468
469 /// Returned when seeking in the input fails (`avformat_seek_file`).
470 #[error("while seeking file: {0}")]
471 SeekFileError(DemuxingError),
472
473 /// Returned when spawning the demuxer thread fails, so demuxing never
474 /// started.
475 #[error("Thread exited")]
476 ThreadExited,
477}
478
479/// Errors from the decoder stage while turning packets into frames.
480/// Variants carrying a [`DecodingError`] embed the mapped FFmpeg error.
481#[derive(thiserror::Error, Debug)]
482#[non_exhaustive]
483pub enum DecodingOperationError {
484 /// Returned when creating a new reference to a decoded frame fails
485 /// (`av_frame_ref`).
486 #[error("during frame reference creation: {0}")]
487 FrameRefError(DecodingError),
488
489 /// Returned when copying frame metadata fails (`av_frame_copy_props`).
490 #[error("during frame properties copy: {0}")]
491 FrameCopyPropsError(DecodingError),
492
493 /// Returned when decoding a subtitle packet fails
494 /// (`avcodec_decode_subtitle2`).
495 #[error("during subtitle decoding: {0}")]
496 DecodeSubtitleError(DecodingError),
497
498 /// Returned when copying a decoded subtitle for delivery fails.
499 #[error("during subtitle copy: {0}")]
500 CopySubtitleError(DecodingError),
501
502 /// Returned when submitting a packet to the decoder fails
503 /// (`avcodec_send_packet`).
504 #[error("during packet submission to decoder: {0}")]
505 SendPacketError(DecodingError),
506
507 /// Returned when receiving a decoded frame from the decoder fails
508 /// (`avcodec_receive_frame`).
509 #[error("during frame reception from decoder: {0}")]
510 ReceiveFrameError(DecodingError),
511
512 /// Returned when allocating a frame during decoding fails.
513 #[error("during frame allocation: {0}")]
514 FrameAllocationError(DecodingError),
515
516 /// Returned when allocating a packet during decoding fails.
517 #[error("during packet allocation: {0}")]
518 PacketAllocationError(DecodingError),
519
520 /// Returned when allocating an `AVSubtitle` during decoding fails.
521 #[error("during AVSubtitle allocation: {0}")]
522 SubtitleAllocationError(DecodingError),
523
524 /// Returned when the decoder emits a frame flagged as corrupt and
525 /// corrupt frames are treated as errors.
526 #[error("corrupt decoded frame")]
527 CorruptFrame,
528
529 /// Returned when the ratio of decode errors to decoded frames exceeds
530 /// the maximum allowed rate.
531 #[error("decode error rate exceeded the maximum allowed")]
532 ErrorRateExceeded,
533
534 /// Returned when downloading a hardware-decoded frame to system memory
535 /// fails (`av_hwframe_transfer_data`).
536 #[error("during retrieve data on hw: {0}")]
537 HWRetrieveDataError(DecodingError),
538
539 /// Returned when applying codec cropping metadata to a decoded frame
540 /// fails.
541 #[error("during cropping: {0}")]
542 CroppingError(DecodingError),
543}
544
545/// Errors from opening and configuring a decoder.
546/// Variants carrying an [`OpenDecoderError`] embed the mapped FFmpeg error.
547#[derive(thiserror::Error, Debug)]
548#[non_exhaustive]
549pub enum OpenDecoderOperationError {
550 /// Returned when allocating the decoder context fails
551 /// (`avcodec_alloc_context3`).
552 #[error("during context allocation: {0}")]
553 ContextAllocationError(OpenDecoderError),
554
555 /// Returned when applying the stream's codec parameters to the decoder
556 /// context fails (`avcodec_parameters_to_context`).
557 #[error("while applying parameters to context: {0}")]
558 ParameterApplicationError(OpenDecoderError),
559
560 /// Returned when opening the decoder fails (`avcodec_open2`).
561 #[error("while opening decoder: {0}")]
562 DecoderOpenError(OpenDecoderError),
563
564 /// Returned when copying the audio channel layout into the decoder
565 /// context fails.
566 #[error("while copying channel layout: {0}")]
567 ChannelLayoutCopyError(OpenDecoderError),
568
569 /// Returned when setting up hardware acceleration for the decoder
570 /// fails.
571 #[error("while Hw setup: {0}")]
572 HwSetupError(OpenDecoderError),
573
574 /// Returned when the configured decoder name is invalid.
575 #[error("Invalid decoder name")]
576 InvalidName,
577
578 /// Returned when spawning the decoder thread fails, so the decoder
579 /// never opened.
580 #[error("Thread exited")]
581 ThreadExited,
582}
583
584/// Errors from running frames through a configured filtergraph.
585/// Variants carrying a [`FilterGraphError`] embed the mapped FFmpeg error.
586#[derive(thiserror::Error, Debug)]
587#[non_exhaustive]
588pub enum FilterGraphOperationError {
589 /// Returned when requesting the next frame from the graph fails
590 /// (`avfilter_graph_request_oldest`).
591 #[error("during requesting oldest frame: {0}")]
592 RequestOldestError(FilterGraphError),
593
594 /// Returned when processing frames through the filtergraph fails.
595 #[error("during process frames: {0}")]
596 ProcessFramesError(FilterGraphError),
597
598 /// Returned when sending frames into the filtergraph fails.
599 #[error("during send frames: {0}")]
600 SendFramesError(FilterGraphError),
601
602 /// Returned when copying an audio channel layout while configuring the
603 /// graph fails.
604 #[error("during copying channel layout: {0}")]
605 ChannelLayoutCopyError(FilterGraphError),
606
607 /// Returned when pushing a frame into a graph input fails
608 /// (`av_buffersrc_add_frame`).
609 #[error("during buffer source add frame: {0}")]
610 BufferSourceAddFrameError(FilterGraphError),
611
612 /// Returned when closing a graph input at end of stream fails
613 /// (`av_buffersrc_close`).
614 #[error("during closing buffer source: {0}")]
615 BufferSourceCloseError(FilterGraphError),
616
617 /// Returned when replacing a frame's buffer reference fails
618 /// (`av_buffer_replace`).
619 #[error("during replace buffer: {0}")]
620 BufferReplaceoseError(FilterGraphError),
621
622 /// Returned when cloning frame side data for the graph fails.
623 #[error("during cloning frame side data: {0}")]
624 FrameSideDataCloneError(FilterGraphError),
625
626 /// Returned when parsing or configuring the filtergraph description
627 /// fails.
628 #[error("during parse: {0}")]
629 ParseError(FilterGraphParseError),
630
631 /// Returned when a frame entering the graph carries invalid or
632 /// corrupted data.
633 #[error("The data in the frame is invalid or corrupted")]
634 InvalidData,
635
636 /// Returned before the graph is configured when one input has buffered
637 /// frames past the admission limit while another input has not yet
638 /// delivered its first frame; fields are the input label, the buffered
639 /// frame count, and the estimated retained memory in bytes.
640 #[error(
641 "graph input '{0}' already holds {1} buffered frames and admitting the next \
642 one would raise the best-effort retained-memory estimate to ~{2} bytes, \
643 while another input has not yet delivered its first frame, so the filter \
644 graph cannot be configured; check that every graph input actually produces \
645 data (or produces it within the buffering window)"
646 )]
647 PreConfigQueueOverflow(String, usize, usize),
648
649 // Only constructed on the FFmpeg 8+ buffersrc side-data clone path.
650 /// Returned when a frame's combined side-data metadata is too large to
651 /// deep-copy into the buffersrc parameters; fields are the input label
652 /// and the estimated size in bytes.
653 #[cfg_attr(not(ffmpeg_8_0), allow(dead_code))]
654 #[error(
655 "graph input '{0}' would deep-copy an estimated {1} bytes of side-data \
656 metadata into the buffersrc parameters, exceeding the side-data clone \
657 estimate threshold; the frame's combined side-data metadata (across its \
658 global and downmix entries) is pathologically large"
659 )]
660 OversizedSideDataClone(String, usize),
661
662 /// Returned when spawning the filtergraph thread fails, so the graph
663 /// never ran.
664 #[error("Thread exited")]
665 ThreadExited,
666}
667
668/// Errors from the encoder stage while turning frames into packets.
669/// Variants carrying an [`EncodingError`] embed the mapped FFmpeg error.
670#[derive(thiserror::Error, Debug)]
671#[non_exhaustive]
672pub enum EncodingOperationError {
673 /// Returned when submitting a frame to the encoder fails
674 /// (`avcodec_send_frame`).
675 #[error("during frame submission: {0}")]
676 SendFrameError(EncodingError),
677
678 /// Returned when receiving an encoded packet from the encoder fails
679 /// (`avcodec_receive_packet`).
680 #[error("during packet retrieval: {0}")]
681 ReceivePacketError(EncodingError),
682
683 /// Returned when re-chunking buffered audio samples into encoder-sized
684 /// frames fails.
685 #[error("during audio frame receive: {0}")]
686 ReceiveAudioError(EncodingError),
687
688 /// Returned when a subtitle packet reaches the encoder without a
689 /// presentation timestamp.
690 #[error(": Subtitle packets must have a pts")]
691 SubtitleNotPts,
692
693 /// Returned when an encoded packet cannot be delivered because the
694 /// muxer has already finished.
695 #[error(": Muxer already finished")]
696 MuxerFinished,
697
698 /// An output stream buffered more packets before the muxer started than the
699 /// pre-mux queue admits (fftools `AVERROR_BUFFER_TOO_SMALL`, "Too many
700 /// packets buffered for output stream"). Unlike `MuxerFinished` this is a
701 /// hard failure — never a silent truncation — so it must reach the
702 /// scheduler error, not the graceful stop path.
703 #[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")]
704 MuxQueueFull,
705
706 /// Returned when encoding a subtitle fails (see
707 /// [`EncodeSubtitleError`]).
708 #[error("Encode subtitle error: {0}")]
709 EncodeSubtitle(#[from] EncodeSubtitleError),
710
711 /// Returned when allocating a packet for encoder output fails.
712 #[error(": {0}")]
713 AllocPacket(AllocPacketError),
714}
715
716/// Errors from the muxer stage while writing the output container.
717/// Variants carrying a [`MuxingError`] embed the mapped FFmpeg error.
718#[derive(thiserror::Error, Debug)]
719#[non_exhaustive]
720pub enum MuxingOperationError {
721 /// Returned when writing the container header fails (see
722 /// [`WriteHeaderError`]).
723 #[error("during write header: {0}")]
724 WriteHeader(WriteHeaderError),
725
726 /// Returned when initializing a bitstream filter chain for an output
727 /// stream fails; fields are the chain description and the underlying
728 /// error.
729 #[error("while initializing bitstream filter chain '{0}': {1}")]
730 BitstreamFilterInit(String, MuxingError),
731
732 /// Returned when writing an interleaved packet to the container fails
733 /// (`av_interleaved_write_frame`).
734 #[error("during interleaved write: {0}")]
735 InterleavedWriteError(MuxingError),
736
737 /// Returned when writing the container trailer fails
738 /// (`av_write_trailer`).
739 #[error("during trailer write: {0}")]
740 TrailerWriteError(MuxingError),
741
742 /// Returned when closing the output I/O context fails.
743 #[error("during closing IO: {0}")]
744 IOCloseError(MuxingError),
745
746 /// Returned when spawning the muxer (or mux-init) thread fails, so
747 /// muxing never started.
748 #[error("Thread exited")]
749 ThreadExited,
750}
751
752/// Errors specific to packet-sink outputs (`Output::new_by_packet_sink`).
753///
754/// The strict tier fails fast: configuration problems surface from `build()`
755/// or from the job **before any sink callback runs**; per-packet violations
756/// stop the job with the offending packet never delivered. `Clone` is
757/// deliberate — for delivery-path errors the same value is recorded as the
758/// job error and handed to the sink's `on_delivery_error` callback.
759/// [`JobFailed`](Self::JobFailed) is the exception: it is synthesized for
760/// that callback only, while first-error-wins may leave the job result owned
761/// by a sibling worker's error.
762#[derive(thiserror::Error, Debug, Clone)]
763#[non_exhaustive]
764pub enum PacketSinkError {
765 /// A builder option the packet sink cannot honor was set: either a
766 /// container-only option (no container is written, so it could never
767 /// take effect) or a pipeline feature outside the strict tier's
768 /// delivery contract (filters, bitstream filters, subtitle codecs —
769 /// rejected as policy, not for lack of a container).
770 #[error("{0} is not supported on packet-sink outputs")]
771 UnsupportedOption(&'static str),
772
773 /// A stream was configured as `copy`; packet sinks require encoded
774 /// streams.
775 #[error("stream copy is not supported on packet-sink outputs (strict tier requires encoded streams)")]
776 StreamCopyUnsupported,
777
778 /// The output mapped a stream the strict tier cannot deliver (non-H.264
779 /// video, non-AAC audio, or a non-audio/video kind).
780 #[error("{kind} streams are not supported on packet-sink outputs (strict tier)")]
781 UnsupportedStream {
782 /// Label describing the rejected stream kind (e.g. "non-H.264
783 /// video", "non-AAC audio").
784 kind: &'static str,
785 },
786
787 /// The configured encoder is outside the strict-tier v1 whitelist.
788 #[error("encoder '{encoder}' is not on the strict-tier whitelist for {kind} (v1 accepts: {allowed})")]
789 EncoderNotWhitelisted {
790 /// Media kind of the stream ("video" or "audio").
791 kind: &'static str,
792 /// The encoder name that was configured.
793 encoder: String,
794 /// The encoder names the strict tier accepts for this kind.
795 allowed: &'static str,
796 },
797
798 /// No stream was mapped to the packet-sink output.
799 #[error("packet-sink output has no streams")]
800 NoStreams,
801
802 /// An encoder finalized without the out-of-band codec configuration the
803 /// strict tier delivers via `on_stream_info`.
804 #[error("output stream {stream_index}: encoder produced no extradata; the strict tier requires codec configuration (avcC / AudioSpecificConfig) before the first callback")]
805 MissingExtradata {
806 /// Index of the offending output stream.
807 stream_index: usize,
808 },
809
810 /// The encoder's codec configuration failed strict-tier validation.
811 #[error("output stream {stream_index}: invalid codec configuration: {reason}")]
812 InvalidExtradata {
813 /// Index of the offending output stream.
814 stream_index: usize,
815 /// Why the codec configuration failed validation.
816 reason: String,
817 },
818
819 /// A stream's time base is not a positive rational.
820 #[error("output stream {stream_index}: invalid time base {num}/{den} (positive numerator and denominator required)")]
821 InvalidTimeBase {
822 /// Index of the offending output stream.
823 stream_index: usize,
824 /// Numerator of the rejected time base.
825 num: i32,
826 /// Denominator of the rejected time base.
827 den: i32,
828 },
829
830 /// A packet was stamped in a time base other than its stream's.
831 #[error("output stream {stream_index}: packet time base {packet_num}/{packet_den} differs from the stream time base {stream_num}/{stream_den}")]
832 PacketTimeBaseMismatch {
833 /// Index of the offending output stream.
834 stream_index: usize,
835 /// Numerator of the packet's time base.
836 packet_num: i32,
837 /// Denominator of the packet's time base.
838 packet_den: i32,
839 /// Numerator of the stream's time base.
840 stream_num: i32,
841 /// Denominator of the stream's time base.
842 stream_den: i32,
843 },
844
845 /// A packet carries no pts or dts (`AV_NOPTS_VALUE`).
846 #[error("output stream {stream_index}: packet carries no {which} (strict tier rejects AV_NOPTS_VALUE)")]
847 MissingTimestamp {
848 /// Index of the offending output stream.
849 stream_index: usize,
850 /// Which timestamp is missing: "pts" or "dts".
851 which: &'static str,
852 },
853
854 /// A packet's dts did not strictly increase within its stream.
855 #[error("output stream {stream_index}: non-monotonic dts (previous {prev}, current {current})")]
856 NonMonotonicDts {
857 /// Index of the offending output stream.
858 stream_index: usize,
859 /// dts of the previous packet, in stream time-base units.
860 prev: i64,
861 /// dts of the offending packet, in stream time-base units.
862 current: i64,
863 },
864
865 /// A packet's pts collided with a still-pending pts on the same stream.
866 #[error("output stream {stream_index}: duplicate pts {pts}")]
867 DuplicatePts {
868 /// Index of the offending output stream.
869 stream_index: usize,
870 /// The duplicated pts value, in stream time-base units.
871 pts: i64,
872 },
873
874 /// A packet's pts is earlier than its dts.
875 #[error("output stream {stream_index}: pts {pts} is earlier than dts {dts}")]
876 PtsBeforeDts {
877 /// Index of the offending output stream.
878 stream_index: usize,
879 /// The packet's pts, in stream time-base units.
880 pts: i64,
881 /// The packet's dts, in stream time-base units.
882 dts: i64,
883 },
884
885 /// Rescaling a timestamp onto the shared time origin overflowed.
886 #[error("output stream {stream_index}: timestamp overflow while applying the shared time origin")]
887 TimestampOverflow {
888 /// Index of the offending output stream.
889 stream_index: usize,
890 },
891
892 /// A packet has no positive duration and none could be derived from the
893 /// stream configuration (frame rate / codec frame size).
894 #[error("output stream {stream_index}: packet duration is absent and cannot be derived (strict tier requires a positive duration)")]
895 MissingDuration {
896 /// Index of the offending output stream.
897 stream_index: usize,
898 },
899
900 /// The packet payload failed bitstream validation.
901 #[error("output stream {stream_index}: malformed packet payload: {reason}")]
902 MalformedPacket {
903 /// Index of the offending output stream.
904 stream_index: usize,
905 /// Description of the bitstream validation failure.
906 reason: String,
907 },
908
909 /// Internal sequencing violation: a packet surfaced outside the delivery
910 /// phase.
911 #[error("output stream {stream_index}: packet processed outside the delivery phase (internal sequencing violation)")]
912 PhaseViolation {
913 /// Index of the offending output stream.
914 stream_index: usize,
915 },
916
917 /// The stream configuration changed after `on_stream_info` delivered it.
918 #[error("output stream {stream_index}: mid-stream configuration change ({what}); the strict tier requires an immutable stream configuration")]
919 ConfigChange {
920 /// Index of the offending output stream.
921 stream_index: usize,
922 /// Description of the configuration change that was detected.
923 what: String,
924 },
925
926 /// An H.264 access unit carried in-band SPS/PPS parameter sets.
927 #[error("output stream {stream_index}: in-band SPS/PPS parameter sets are not supported in the strict tier (WebCodecs avc requires out-of-band configuration)")]
928 InBandParameterSets {
929 /// Index of the offending output stream.
930 stream_index: usize,
931 },
932
933 /// The sink's `on_stream_info` callback rejected the configuration.
934 #[error("on_stream_info callback rejected the stream configuration: {error}")]
935 StreamInfoCallbackFailed {
936 /// The error the callback returned.
937 #[source]
938 error: crate::core::packet_sink::PacketCallbackError,
939 },
940
941 /// The sink's `on_packet` callback returned an error.
942 #[error("on_packet callback failed on output stream {stream_index}: {error}")]
943 PacketCallbackFailed {
944 /// Index of the offending output stream.
945 stream_index: usize,
946 /// The error the callback returned.
947 #[source]
948 error: crate::core::packet_sink::PacketCallbackError,
949 },
950
951 /// The channel adapter's receiver was dropped, cancelling delivery and
952 /// the job.
953 #[error("the packet-sink channel receiver was dropped; delivery cancelled")]
954 ChannelDisconnected,
955
956 /// The job failed outside this sink's delivery path; handed to
957 /// `on_delivery_error` only, while `wait()` keeps the original error.
958 #[error("the job failed outside this packet sink; delivery may have been truncated: {message}")]
959 JobFailed {
960 /// Display rendering of the error that actually failed the job.
961 message: String,
962 },
963}
964
965/// Errors from opening and configuring an encoder.
966/// Variants carrying an [`OpenEncoderError`] embed the mapped FFmpeg error.
967#[derive(thiserror::Error, Debug)]
968#[non_exhaustive]
969pub enum OpenEncoderOperationError {
970 /// Returned when cloning frame side data into the encoder context
971 /// fails.
972 #[error("during frame side data cloning: {0}")]
973 FrameSideDataCloneError(OpenEncoderError),
974
975 /// Returned when copying the audio channel layout into the encoder
976 /// context fails.
977 #[error("during channel layout copying: {0}")]
978 ChannelLayoutCopyError(OpenEncoderError),
979
980 /// Returned when opening the encoder fails (`avcodec_open2`).
981 #[error("during codec opening: {0}")]
982 CodecOpenError(OpenEncoderError),
983
984 /// Returned when exporting encoder parameters to the output stream
985 /// fails (`avcodec_parameters_from_context`).
986 #[error("while setting codec parameters: {0}")]
987 CodecParametersError(OpenEncoderError),
988
989 /// Returned when the format of the frame to encode is unknown.
990 #[error(": unknown format of the frame")]
991 UnknownFrameFormat,
992
993 /// Returned when configuring subtitle encoding parameters fails.
994 #[error("while setting subtitle: {0}")]
995 SettingSubtitleError(OpenEncoderError),
996
997 /// Returned when setting up hardware acceleration for the encoder
998 /// fails.
999 #[error("while Hw setup: {0}")]
1000 HwSetupError(OpenEncoderError),
1001
1002 /// Returned when allocating the encoder context fails
1003 /// (`avcodec_alloc_context3`).
1004 #[error("during context allocation: {0}")]
1005 ContextAllocationError(OpenEncoderError),
1006
1007 /// Returned when the frame stream ends (EOF or upstream disconnect)
1008 /// before the encoder received any frame, so the encoder was never
1009 /// opened.
1010 #[error(": no frames were received before EOF; encoder never opened")]
1011 NoFramesReceived,
1012
1013 /// Returned when the stream's media type cannot be encoded (not video,
1014 /// audio, or subtitle).
1015 #[error(": unsupported media type for encoding")]
1016 UnsupportedMediaType,
1017
1018 /// Returned when spawning the encoder thread fails, so the encoder
1019 /// never started.
1020 #[error("Thread exited")]
1021 ThreadExited,
1022}
1023
1024/// Errors from converting URL or path strings for FFmpeg.
1025#[derive(thiserror::Error, Debug)]
1026#[non_exhaustive]
1027pub enum UrlError {
1028 /// Returned when the string contains an interior NUL byte, which C
1029 /// strings cannot represent; the payload is the byte position.
1030 #[error("Null byte found in string at position {0}")]
1031 NullByteError(usize),
1032}
1033
1034impl From<NulError> for Error {
1035 fn from(err: NulError) -> Self {
1036 Error::Url(UrlError::NullByteError(err.nul_position()))
1037 }
1038}
1039
1040/// Errors from opening an input file, stream, device, or custom input
1041/// source. Most variants are mapped from the FFmpeg error code returned by
1042/// `avformat_open_input`.
1043#[derive(thiserror::Error, Debug)]
1044#[non_exhaustive]
1045pub enum OpenInputError {
1046 /// Memory allocation failed (`AVERROR(ENOMEM)`).
1047 #[error("Memory allocation error")]
1048 OutOfMemory,
1049
1050 /// FFmpeg rejected an argument as invalid (`AVERROR(EINVAL)`).
1051 #[error("Invalid argument provided")]
1052 InvalidArgument,
1053
1054 /// The file, URL, or device does not exist (`AVERROR(ENOENT)`).
1055 #[error("File or stream not found")]
1056 NotFound,
1057
1058 /// A low-level I/O error occurred (`AVERROR(EIO)`).
1059 #[error("I/O error occurred while opening the file or stream")]
1060 IOError,
1061
1062 /// The stream or data connection was broken (`AVERROR(EPIPE)`).
1063 #[error("Pipe error, possibly the stream or data connection was broken")]
1064 PipeError,
1065
1066 /// An invalid file descriptor was used (`AVERROR(EBADF)`).
1067 #[error("Invalid file descriptor")]
1068 BadFileDescriptor,
1069
1070 /// The functionality or input format is not supported by the linked
1071 /// FFmpeg build (`AVERROR(ENOSYS)`).
1072 #[error("Functionality not implemented or unsupported input format")]
1073 NotImplemented,
1074
1075 /// The operation was not permitted (`AVERROR(EPERM)`).
1076 #[error("Operation not permitted to access the file or stream")]
1077 OperationNotPermitted,
1078
1079 /// The file or stream contains invalid or corrupted data
1080 /// (`AVERROR_INVALIDDATA`).
1081 #[error("The data in the file or stream is invalid or corrupted")]
1082 InvalidData,
1083
1084 /// The connection timed out (`AVERROR(ETIMEDOUT)`).
1085 #[error("The connection timed out while trying to open the stream")]
1086 Timeout,
1087
1088 /// A builder option carried an invalid value (e.g. a non-positive
1089 /// `set_framerate`, a non-finite `set_ts_scale`, an out-of-range
1090 /// `set_io_buffer_size`). Setters store values as given and defer
1091 /// validation to open time, so a bad value surfaces here instead of
1092 /// panicking in the setter.
1093 #[error("Invalid input option: {0}")]
1094 InvalidOption(String),
1095
1096 /// Any other failure; the payload is the raw FFmpeg error code
1097 /// (rendered with `av_err2str` in the message).
1098 #[error("{}. ret:{0}", crate::util::ffmpeg_utils::av_err2str(*.0))]
1099 UnknownError(i32),
1100
1101 /// Returned when the input has no usable source: neither a URL nor a
1102 /// custom read callback was configured.
1103 #[error("Invalid source provided")]
1104 InvalidSource,
1105
1106 /// Returned when the explicitly requested input format name is unknown
1107 /// to FFmpeg; the payload is the requested name.
1108 #[error("Invalid source format:{0}")]
1109 InvalidFormat(String),
1110
1111 /// Returned when the input requires seeking but the custom input source
1112 /// provides no seek callback.
1113 #[error("No seek callback is provided")]
1114 SeekFunctionMissing,
1115}
1116
1117impl From<i32> for OpenInputError {
1118 fn from(err_code: i32) -> Self {
1119 match err_code {
1120 AVERROR_OUT_OF_MEMORY => OpenInputError::OutOfMemory,
1121 AVERROR_INVALID_ARGUMENT => OpenInputError::InvalidArgument,
1122 AVERROR_NOT_FOUND => OpenInputError::NotFound,
1123 AVERROR_IO_ERROR => OpenInputError::IOError,
1124 AVERROR_PIPE_ERROR => OpenInputError::PipeError,
1125 AVERROR_BAD_FILE_DESCRIPTOR => OpenInputError::BadFileDescriptor,
1126 AVERROR_NOT_IMPLEMENTED => OpenInputError::NotImplemented,
1127 AVERROR_OPERATION_NOT_PERMITTED => OpenInputError::OperationNotPermitted,
1128 AVERROR_INVALIDDATA => OpenInputError::InvalidData,
1129 AVERROR_TIMEOUT => OpenInputError::Timeout,
1130 _ => OpenInputError::UnknownError(err_code),
1131 }
1132 }
1133}
1134
1135const AVERROR_OUT_OF_MEMORY: i32 = AVERROR(ENOMEM);
1136const AVERROR_INVALID_ARGUMENT: i32 = AVERROR(EINVAL);
1137const AVERROR_NOT_FOUND: i32 = AVERROR(ENOENT);
1138const AVERROR_IO_ERROR: i32 = AVERROR(EIO);
1139const AVERROR_PIPE_ERROR: i32 = AVERROR(EPIPE);
1140const AVERROR_BAD_FILE_DESCRIPTOR: i32 = AVERROR(EBADF);
1141const AVERROR_NOT_IMPLEMENTED: i32 = AVERROR(ENOSYS);
1142const AVERROR_OPERATION_NOT_PERMITTED: i32 = AVERROR(EPERM);
1143const AVERROR_PERMISSION_DENIED: i32 = AVERROR(EACCES);
1144const AVERROR_TIMEOUT: i32 = AVERROR(ETIMEDOUT);
1145const AVERROR_NOT_SOCKET: i32 = AVERROR(ENOTSOCK);
1146const AVERROR_AGAIN: i32 = AVERROR(EAGAIN);
1147
1148/// Errors from probing stream information after an input is opened
1149/// (`avformat_find_stream_info`).
1150#[derive(thiserror::Error, Debug)]
1151#[non_exhaustive]
1152pub enum FindStreamError {
1153 /// Memory allocation failed (`AVERROR(ENOMEM)`).
1154 #[error("Memory allocation error")]
1155 OutOfMemory,
1156
1157 /// FFmpeg rejected an argument as invalid (`AVERROR(EINVAL)`).
1158 #[error("Invalid argument provided")]
1159 InvalidArgument,
1160
1161 /// Reached end of file before stream information could be determined
1162 /// (`AVERROR_EOF`).
1163 #[error("Reached end of file while looking for stream info")]
1164 EndOfFile,
1165
1166 /// The operation timed out (`AVERROR(ETIMEDOUT)`).
1167 #[error("Timeout occurred while reading stream info")]
1168 Timeout,
1169
1170 /// A low-level I/O error occurred (`AVERROR(EIO)`).
1171 #[error("I/O error occurred while reading stream info")]
1172 IOError,
1173
1174 /// The stream contains invalid or corrupted data
1175 /// (`AVERROR_INVALIDDATA`).
1176 #[error("The data in the stream is invalid or corrupted")]
1177 InvalidData,
1178
1179 /// The functionality or stream format is not supported by the linked
1180 /// FFmpeg build (`AVERROR(ENOSYS)`).
1181 #[error("Functionality not implemented or unsupported stream format")]
1182 NotImplemented,
1183
1184 /// The operation was not permitted (`AVERROR(EPERM)`).
1185 #[error("Operation not permitted to access the file or stream")]
1186 OperationNotPermitted,
1187
1188 /// Returned when the input contains no streams, or no stream of the
1189 /// requested kind.
1190 #[error("No Stream found")]
1191 NoStreamFound,
1192
1193 /// Any other failure; the payload is the raw FFmpeg error code
1194 /// (rendered with `av_err2str` in the message).
1195 #[error("{}. ret:{0}", crate::util::ffmpeg_utils::av_err2str(*.0))]
1196 UnknownError(i32),
1197}
1198
1199impl From<i32> for FindStreamError {
1200 fn from(err_code: i32) -> Self {
1201 match err_code {
1202 AVERROR_OUT_OF_MEMORY => FindStreamError::OutOfMemory,
1203 AVERROR_INVALID_ARGUMENT => FindStreamError::InvalidArgument,
1204 AVERROR_EOF => FindStreamError::EndOfFile,
1205 AVERROR_TIMEOUT => FindStreamError::Timeout,
1206 AVERROR_IO_ERROR => FindStreamError::IOError,
1207 AVERROR_INVALIDDATA => FindStreamError::InvalidData,
1208 AVERROR_NOT_IMPLEMENTED => FindStreamError::NotImplemented,
1209 AVERROR_OPERATION_NOT_PERMITTED => FindStreamError::OperationNotPermitted,
1210 _ => FindStreamError::UnknownError(err_code),
1211 }
1212 }
1213}
1214
1215/// Errors from parsing a filtergraph description and wiring its inputs and
1216/// outputs.
1217#[derive(thiserror::Error, Debug)]
1218#[non_exhaustive]
1219pub enum FilterGraphParseError {
1220 /// Memory allocation failed (`AVERROR(ENOMEM)`).
1221 #[error("Memory allocation error")]
1222 OutOfMemory,
1223
1224 /// FFmpeg rejected an argument as invalid (`AVERROR(EINVAL)`).
1225 #[error("Invalid argument provided")]
1226 InvalidArgument,
1227
1228 /// End of file was reached during parsing (`AVERROR_EOF`).
1229 #[error("End of file reached during parsing")]
1230 EndOfFile,
1231
1232 /// A low-level I/O error occurred (`AVERROR(EIO)`).
1233 #[error("I/O error occurred during parsing")]
1234 IOError,
1235
1236 /// Invalid data was encountered during parsing
1237 /// (`AVERROR_INVALIDDATA`).
1238 #[error("Invalid data encountered during parsing")]
1239 InvalidData,
1240
1241 /// The functionality or filter is not supported by the linked FFmpeg
1242 /// build (`AVERROR(ENOSYS)`).
1243 #[error("Functionality not implemented or unsupported filter format")]
1244 NotImplemented,
1245
1246 /// Permission was denied — e.g. by a filter that opens files, such as
1247 /// `movie=` (`AVERROR(EACCES)`).
1248 #[error("Permission denied during filter graph parsing")]
1249 PermissionDenied,
1250
1251 /// A socket operation was attempted on a non-socket by a filter
1252 /// touching network resources (`AVERROR(ENOTSOCK)`).
1253 #[error("Socket operation on non-socket during filter graph parsing")]
1254 NotSocket,
1255
1256 /// A filter option named in the description does not exist
1257 /// (`AVERROR_OPTION_NOT_FOUND`).
1258 #[error("Option not found during filter graph configuration")]
1259 OptionNotFound,
1260
1261 /// Returned when a stream reference in the filtergraph description
1262 /// names an input file index that does not exist; fields are the index
1263 /// and the description.
1264 #[error("Invalid file index {0} in filtergraph description {1}")]
1265 InvalidFileIndexInFg(usize, String),
1266
1267 /// Returned when an output URL references an input file index that does
1268 /// not exist; fields are the index and the URL.
1269 #[error("Invalid file index {0} in output url: {1}")]
1270 InvalidFileIndexInOutput(usize, String),
1271
1272 /// Returned when a stream specifier in the filtergraph description is
1273 /// malformed; the payload is the offending text.
1274 #[error("Invalid filter specifier {0}")]
1275 InvalidFilterSpecifier(String),
1276
1277 /// Returned when a filtergraph output pad is not connected to any
1278 /// output; fields are the filter name, the pad index, and its link
1279 /// label.
1280 #[error("Filter '{0}' has output {1} ({2}) unconnected")]
1281 OutputUnconnected(String, usize, String),
1282
1283 /// Any other failure; the payload is the raw FFmpeg error code.
1284 #[error("An unknown error occurred. ret: {0}")]
1285 UnknownError(i32),
1286}
1287
1288impl From<i32> for FilterGraphParseError {
1289 fn from(err_code: i32) -> Self {
1290 match err_code {
1291 AVERROR_OUT_OF_MEMORY => FilterGraphParseError::OutOfMemory,
1292 AVERROR_INVALID_ARGUMENT => FilterGraphParseError::InvalidArgument,
1293 AVERROR_EOF => FilterGraphParseError::EndOfFile,
1294 AVERROR_IO_ERROR => FilterGraphParseError::IOError,
1295 AVERROR_INVALIDDATA => FilterGraphParseError::InvalidData,
1296 AVERROR_NOT_IMPLEMENTED => FilterGraphParseError::NotImplemented,
1297 AVERROR_OPTION_NOT_FOUND => FilterGraphParseError::OptionNotFound,
1298 // EACCES/ENOTSOCK reach here from filters that touch files or
1299 // sockets (e.g. `movie=`); map them to the variants this enum
1300 // already declares instead of degrading to UnknownError.
1301 AVERROR_PERMISSION_DENIED => FilterGraphParseError::PermissionDenied,
1302 AVERROR_NOT_SOCKET => FilterGraphParseError::NotSocket,
1303 _ => FilterGraphParseError::UnknownError(err_code),
1304 }
1305 }
1306}
1307
1308/// Errors from allocating an output format context
1309/// (`avformat_alloc_output_context2`).
1310#[derive(thiserror::Error, Debug)]
1311#[non_exhaustive]
1312pub enum AllocOutputContextError {
1313 /// Memory allocation failed (`AVERROR(ENOMEM)`).
1314 #[error("Memory allocation error")]
1315 OutOfMemory,
1316
1317 /// FFmpeg rejected an argument as invalid (`AVERROR(EINVAL)`).
1318 #[error("Invalid argument provided")]
1319 InvalidArgument,
1320
1321 /// The file or stream does not exist (`AVERROR(ENOENT)`).
1322 #[error("File or stream not found")]
1323 NotFound,
1324
1325 /// A low-level I/O error occurred (`AVERROR(EIO)`).
1326 #[error("I/O error occurred while allocating the output context")]
1327 IOError,
1328
1329 /// The stream or data connection was broken (`AVERROR(EPIPE)`).
1330 #[error("Pipe error, possibly the stream or data connection was broken")]
1331 PipeError,
1332
1333 /// An invalid file descriptor was used (`AVERROR(EBADF)`).
1334 #[error("Invalid file descriptor")]
1335 BadFileDescriptor,
1336
1337 /// The functionality or output format is not supported by the linked
1338 /// FFmpeg build (`AVERROR(ENOSYS)`).
1339 #[error("Functionality not implemented or unsupported output format")]
1340 NotImplemented,
1341
1342 /// The operation was not permitted (`AVERROR(EPERM)`).
1343 #[error("Operation not permitted to allocate the output context")]
1344 OperationNotPermitted,
1345
1346 /// Permission was denied (`AVERROR(EACCES)`).
1347 #[error("Permission denied while allocating the output context")]
1348 PermissionDenied,
1349
1350 /// The operation timed out (`AVERROR(ETIMEDOUT)`).
1351 #[error("The connection timed out while trying to allocate the output context")]
1352 Timeout,
1353
1354 /// Any other failure; the payload is the raw FFmpeg error code
1355 /// (rendered with `av_err2str` in the message).
1356 #[error("{}. ret:{0}", crate::util::ffmpeg_utils::av_err2str(*.0))]
1357 UnknownError(i32),
1358}
1359
1360impl From<i32> for AllocOutputContextError {
1361 fn from(err_code: i32) -> Self {
1362 match err_code {
1363 AVERROR_OUT_OF_MEMORY => AllocOutputContextError::OutOfMemory,
1364 AVERROR_INVALID_ARGUMENT => AllocOutputContextError::InvalidArgument,
1365 AVERROR_NOT_FOUND => AllocOutputContextError::NotFound,
1366 AVERROR_IO_ERROR => AllocOutputContextError::IOError,
1367 AVERROR_PIPE_ERROR => AllocOutputContextError::PipeError,
1368 AVERROR_BAD_FILE_DESCRIPTOR => AllocOutputContextError::BadFileDescriptor,
1369 AVERROR_NOT_IMPLEMENTED => AllocOutputContextError::NotImplemented,
1370 AVERROR_OPERATION_NOT_PERMITTED => AllocOutputContextError::OperationNotPermitted,
1371 AVERROR_PERMISSION_DENIED => AllocOutputContextError::PermissionDenied,
1372 AVERROR_TIMEOUT => AllocOutputContextError::Timeout,
1373 _ => AllocOutputContextError::UnknownError(err_code),
1374 }
1375 }
1376}
1377
1378/// Errors from opening and configuring an output: resolving formats and
1379/// encoders, mapping streams, validating options, and opening the target
1380/// for writing.
1381#[derive(thiserror::Error, Debug)]
1382#[non_exhaustive]
1383pub enum OpenOutputError {
1384 /// Memory allocation failed (`AVERROR(ENOMEM)`).
1385 #[error("Memory allocation error")]
1386 OutOfMemory,
1387
1388 /// FFmpeg rejected an argument as invalid (`AVERROR(EINVAL)`).
1389 #[error("Invalid argument provided")]
1390 InvalidArgument,
1391
1392 /// The file or stream does not exist (`AVERROR(ENOENT)`).
1393 #[error("File or stream not found")]
1394 NotFound,
1395
1396 /// A low-level I/O error occurred (`AVERROR(EIO)`).
1397 #[error("I/O error occurred while opening the file or stream")]
1398 IOError,
1399
1400 /// The stream or data connection was broken (`AVERROR(EPIPE)`).
1401 #[error("Pipe error, possibly the stream or data connection was broken")]
1402 PipeError,
1403
1404 /// An invalid file descriptor was used (`AVERROR(EBADF)`).
1405 #[error("Invalid file descriptor")]
1406 BadFileDescriptor,
1407
1408 /// The functionality or output format is not supported by the linked
1409 /// FFmpeg build (`AVERROR(ENOSYS)`).
1410 #[error("Functionality not implemented or unsupported output format")]
1411 NotImplemented,
1412
1413 /// The operation was not permitted (`AVERROR(EPERM)`).
1414 #[error("Operation not permitted to open the file or stream")]
1415 OperationNotPermitted,
1416
1417 /// Permission was denied (`AVERROR(EACCES)`).
1418 #[error("Permission denied while opening the file or stream")]
1419 PermissionDenied,
1420
1421 /// The operation timed out (`AVERROR(ETIMEDOUT)`).
1422 #[error("The connection timed out while trying to open the file or stream")]
1423 Timeout,
1424
1425 /// No encoder was found for the requested codec
1426 /// (`AVERROR_ENCODER_NOT_FOUND`).
1427 #[error("encoder not found")]
1428 EncoderNotFound,
1429
1430 /// A named encoder could not be opened because the linked FFmpeg build
1431 /// does not provide it — either it was compiled without that encoder
1432 /// (e.g. no `--enable-libx264`) or the name is not a known encoder at all.
1433 /// Unlike the bare [`EncoderNotFound`](Self::EncoderNotFound) errno
1434 /// mapping, this names the encoder so the fix is actionable. `name` is the
1435 /// encoder the caller requested, or the codec the output format guessed
1436 /// when none was set explicitly.
1437 #[error(
1438 "encoder '{name}' is not available in the linked FFmpeg build — link \
1439 an FFmpeg build that provides it (for example one configured with \
1440 --enable-libx264 for libx264), or select a different encoder via \
1441 Output::set_video_codec / set_audio_codec / set_subtitle_codec \
1442 (list what the build provides with codec::get_encoders)"
1443 )]
1444 EncoderUnavailable {
1445 /// The requested encoder name, or the format's guessed default
1446 /// codec when none was set explicitly.
1447 name: String,
1448 },
1449
1450 /// Returned when a stream map specifier matches no streams; the payload
1451 /// is the specifier.
1452 #[error("Stream map '{0}' matches no streams;")]
1453 MatchesNoStreams(String),
1454
1455 /// A stream map combined stream copy with a per-map re-encoding
1456 /// request ([`StreamMap::codec`] / [`StreamMap::codec_opt`]): copied
1457 /// packets never pass through an encoder, so a per-map codec or
1458 /// per-map codec options could never take effect. Raised at `build()`
1459 /// instead of silently ignoring the request (the FFmpeg CLI merely
1460 /// warns about such unused options).
1461 ///
1462 /// [`StreamMap::codec`]: crate::core::context::output::StreamMap::codec
1463 /// [`StreamMap::codec_opt`]: crate::core::context::output::StreamMap::codec_opt
1464 #[error(
1465 "stream map '{spec}' requests stream copy together with {what}; \
1466 stream copy and per-map re-encoding settings are mutually exclusive"
1467 )]
1468 StreamMapCopyConflict {
1469 /// The offending stream map specifier.
1470 spec: String,
1471 /// The per-map re-encoding setting that conflicts with copy.
1472 what: &'static str,
1473 },
1474
1475 /// Returned when an output references an invalid filtergraph link
1476 /// label; the payload is the label.
1477 #[error("Invalid label {0}")]
1478 InvalidLabel(String),
1479
1480 /// Returned when the output ends up with no streams at all.
1481 #[error("not contain any stream")]
1482 NotContainStream,
1483
1484 /// Returned when the format of the frame feeding an output stream is
1485 /// unknown, so encoder parameters cannot be derived from it.
1486 #[error("unknown format of the frame")]
1487 UnknownFrameFormat,
1488
1489 /// Returned when an input URL references a file index that does not
1490 /// exist; fields are the index and the URL.
1491 #[error("Invalid file index {0} in input url: {1}")]
1492 InvalidFileIndexInIntput(usize, String),
1493
1494 /// Any other failure; the payload is the raw FFmpeg error code
1495 /// (rendered with `av_err2str` in the message).
1496 #[error("{}. ret:{0}", crate::util::ffmpeg_utils::av_err2str(*.0))]
1497 UnknownError(i32),
1498
1499 /// Returned when the output has no usable destination: neither a URL
1500 /// nor a custom write callback was configured.
1501 #[error("Invalid sink provided")]
1502 InvalidSink,
1503
1504 /// Returned when the output format requires seeking but the custom
1505 /// output sink provides no seek callback.
1506 #[error("No seek callback is provided")]
1507 SeekFunctionMissing,
1508
1509 /// Returned when the requested output format name is unknown to FFmpeg;
1510 /// the payload is the requested name.
1511 #[error("Format '{0}' is unsupported")]
1512 FormatUnsupported(String),
1513
1514 /// Returned when a pixel format name is not recognized; the payload is
1515 /// the name.
1516 #[error("Unknown pixel format: '{0}'")]
1517 UnknownPixelFormat(String),
1518
1519 /// Returned when a sample format name is not recognized; the payload is
1520 /// the name.
1521 #[error("Unknown sample format: '{0}'")]
1522 UnknownSampleFormat(String),
1523
1524 /// A builder option carried an invalid value (e.g. a malformed
1525 /// `set_force_key_frames` spec, an out-of-range `set_io_buffer_size`).
1526 /// Setters store values as given and defer validation to open time, so
1527 /// a bad value surfaces here instead of panicking in the setter or
1528 /// forcing a `Result` into the middle of a builder chain.
1529 #[error("Invalid output option: {0}")]
1530 InvalidOption(String),
1531
1532 /// Returned when reading an attachment file fails; the payload is the
1533 /// path, with the underlying I/O error as the source.
1534 #[error("Failed to read attachment file '{0}'")]
1535 AttachmentRead(String, #[source] io::Error),
1536
1537 /// Returned when an attachment file is empty; the payload is the path.
1538 #[error("Attachment file '{0}' is empty")]
1539 AttachmentEmpty(String),
1540
1541 /// Returned when an attachment file exceeds the size limit; fields are
1542 /// the path, its size in bytes, and the limit in bytes.
1543 #[error("Attachment file '{0}' is too large ({1} bytes, limit {2} bytes)")]
1544 AttachmentTooLarge(String, u64, u64),
1545
1546 /// Returned when an attachment was configured with an empty mimetype;
1547 /// the payload is the file path.
1548 #[error("Attachment mimetype must not be empty (file '{0}')")]
1549 AttachmentEmptyMimetype(String),
1550
1551 /// A per-output video filter ([`Output::set_video_filter`]) was combined
1552 /// with stream copy for the same output's video — either
1553 /// `set_video_codec("copy")` or a copy stream map covering a video
1554 /// stream. Mirrors the FFmpeg CLI error for `-vf` + `-c:v copy`
1555 /// ("Filtering and streamcopy cannot be used together",
1556 /// ffmpeg_mux_init.c streamcopy_init).
1557 ///
1558 /// [`Output::set_video_filter`]: crate::core::context::output::Output::set_video_filter
1559 #[error(
1560 "Filtergraph '{0}' was specified, but codec copy was selected for the \
1561 output's video stream. Filtering and streamcopy cannot be used together"
1562 )]
1563 FilterWithStreamCopy(String),
1564
1565 /// A per-output video filter ([`Output::set_video_filter`]) was set on an
1566 /// output whose video stream is fed by a context-level filtergraph
1567 /// (`FfmpegContextBuilder::filter_desc`). Mirrors the FFmpeg CLI error for
1568 /// `-vf` + `-filter_complex` on the same stream (ffmpeg_mux_init.c
1569 /// ost_get_filters: "Simple and complex filtering cannot be used together
1570 /// for the same stream").
1571 ///
1572 /// [`Output::set_video_filter`]: crate::core::context::output::Output::set_video_filter
1573 #[error(
1574 "Filtergraph '{0}' was specified for a video stream fed from a \
1575 context-level filtergraph. Simple and complex filtering cannot be \
1576 used together for the same stream"
1577 )]
1578 SimpleAndComplexFilter(String),
1579
1580 /// A per-output simple filtergraph must be one connected linear chain:
1581 /// exactly one video input pad, one video output pad, a single connected
1582 /// component, and a directed path from the input to the output (fftools
1583 /// fg_create_simple's contract plus the topology rules a simple graph
1584 /// implies — a disconnected or unreachable description would encode
1585 /// unrelated frames or hang instead of filtering the stream). The path
1586 /// requirement is structural: the input pad must be wired into the flow
1587 /// that feeds the output pad, while a filter that may discard it at
1588 /// runtime (`streamselect` whose applied `map` selects another input —
1589 /// rewritable mid-stream via `sendcmd`) is accepted, matching the CLI.
1590 /// `reason` names the violated rule. Descriptions that split, merge or
1591 /// source streams belong in the context-level `filter_desc`.
1592 #[error(
1593 "Simple filtergraph '{desc}' is not a single connected chain: {reason}; \
1594 use FfmpegContextBuilder::filter_desc for complex graphs"
1595 )]
1596 SimpleFilterInvalidShape {
1597 /// The offending filtergraph description, as configured.
1598 desc: String,
1599 /// The topology rule the description violates.
1600 reason: String,
1601 },
1602
1603 /// A configured [`Output::set_video_filter`] chain that no re-encoded
1604 /// video stream ended up consuming: the output has no video stream at all
1605 /// (audio-only input, `disable_video()`, or maps that matched no video
1606 /// stream). The ffmpeg CLI silently ignores `-vf` in that situation; the
1607 /// crate refuses instead of dropping configuration on the floor.
1608 ///
1609 /// [`Output::set_video_filter`]: crate::core::context::output::Output::set_video_filter
1610 #[error(
1611 "video filter '{0}' was configured, but the output ended up with no \
1612 re-encoded video stream to run it (audio-only input, disable_video(), \
1613 or maps matching no video stream); remove the filter or map a video \
1614 stream"
1615 )]
1616 VideoFilterUnused(String),
1617
1618 /// A per-output simple filtergraph's pads must match the stream's media
1619 /// type (fftools fg_create_simple: "Filtergraph has a %s output, cannot
1620 /// connect it to %s output stream") — e.g. an audio chain like `anull`
1621 /// cannot be attached as a video filter.
1622 ///
1623 /// The media-type labels are static (`"video"`, `"audio"`, ... — the
1624 /// strings fftools prints), which keeps this variant inside `Error`'s
1625 /// 64-byte layout; three owned `String`s would grow every hot-path
1626 /// `Result` in the crate.
1627 #[error(
1628 "Simple filtergraph '{desc}' has a {found} pad, cannot connect it to \
1629 the {expected} stream of this output"
1630 )]
1631 SimpleFilterMediaTypeMismatch {
1632 /// The offending filtergraph description, as configured.
1633 desc: String,
1634 /// The media type of the mismatched pad.
1635 found: &'static str,
1636 /// The media type the output stream requires.
1637 expected: &'static str,
1638 },
1639}
1640
1641impl From<i32> for OpenOutputError {
1642 fn from(err_code: i32) -> Self {
1643 match err_code {
1644 AVERROR_OUT_OF_MEMORY => OpenOutputError::OutOfMemory,
1645 AVERROR_INVALID_ARGUMENT => OpenOutputError::InvalidArgument,
1646 AVERROR_NOT_FOUND => OpenOutputError::NotFound,
1647 AVERROR_IO_ERROR => OpenOutputError::IOError,
1648 AVERROR_PIPE_ERROR => OpenOutputError::PipeError,
1649 AVERROR_BAD_FILE_DESCRIPTOR => OpenOutputError::BadFileDescriptor,
1650 AVERROR_NOT_IMPLEMENTED => OpenOutputError::NotImplemented,
1651 AVERROR_OPERATION_NOT_PERMITTED => OpenOutputError::OperationNotPermitted,
1652 AVERROR_PERMISSION_DENIED => OpenOutputError::PermissionDenied,
1653 AVERROR_TIMEOUT => OpenOutputError::Timeout,
1654 AVERROR_ENCODER_NOT_FOUND => OpenOutputError::EncoderNotFound,
1655 _ => OpenOutputError::UnknownError(err_code),
1656 }
1657 }
1658}
1659
1660/// Errors from enumerating capture devices (cameras, microphones,
1661/// screens).
1662#[derive(thiserror::Error, Debug)]
1663#[non_exhaustive]
1664pub enum FindDevicesError {
1665 /// Returned on macOS when the `AVCaptureDevice` class is not available.
1666 #[error("AVCaptureDevice class not found in macOS")]
1667 AVCaptureDeviceNotFound,
1668
1669 /// Returned when device enumeration for the requested media type is not
1670 /// supported; the payload is the raw `AVMediaType` value.
1671 #[error("current media_type({0}) is not supported")]
1672 MediaTypeSupported(i32),
1673 /// Returned when device enumeration is not supported on the current
1674 /// operating system.
1675 #[error("current OS is not supported")]
1676 OsNotSupported,
1677 /// Returned when a device description could not be converted to a UTF-8
1678 /// string.
1679 #[error("device_description can not to string")]
1680 UTF8Error,
1681
1682 /// Memory allocation failed (`AVERROR(ENOMEM)`).
1683 #[error("Memory allocation error")]
1684 OutOfMemory,
1685 /// FFmpeg rejected an argument as invalid (`AVERROR(EINVAL)`).
1686 #[error("Invalid argument provided")]
1687 InvalidArgument,
1688 /// The device or stream does not exist (`AVERROR(ENOENT)`).
1689 #[error("Device or stream not found")]
1690 NotFound,
1691 /// A low-level I/O error occurred (`AVERROR(EIO)`).
1692 #[error("I/O error occurred while accessing the device or stream")]
1693 IOError,
1694 /// The operation was not permitted (`AVERROR(EPERM)`).
1695 #[error("Operation not permitted for this device or stream")]
1696 OperationNotPermitted,
1697 /// Permission was denied (`AVERROR(EACCES)`).
1698 #[error("Permission denied while accessing the device or stream")]
1699 PermissionDenied,
1700 /// The functionality is not supported by the linked FFmpeg build
1701 /// (`AVERROR(ENOSYS)`).
1702 #[error("This functionality is not implemented")]
1703 NotImplemented,
1704 /// An invalid file descriptor was used (`AVERROR(EBADF)`).
1705 #[error("Bad file descriptor")]
1706 BadFileDescriptor,
1707 /// Any other failure; the payload is the raw FFmpeg error code
1708 /// (rendered with `av_err2str` in the message).
1709 #[error("{}. ret:{0}", crate::util::ffmpeg_utils::av_err2str(*.0))]
1710 UnknownError(i32),
1711}
1712
1713impl From<i32> for FindDevicesError {
1714 fn from(err_code: i32) -> Self {
1715 match err_code {
1716 AVERROR_OUT_OF_MEMORY => FindDevicesError::OutOfMemory,
1717 AVERROR_INVALID_ARGUMENT => FindDevicesError::InvalidArgument,
1718 AVERROR_NOT_FOUND => FindDevicesError::NotFound,
1719 AVERROR_IO_ERROR => FindDevicesError::IOError,
1720 AVERROR_OPERATION_NOT_PERMITTED => FindDevicesError::OperationNotPermitted,
1721 AVERROR_PERMISSION_DENIED => FindDevicesError::PermissionDenied,
1722 AVERROR_NOT_IMPLEMENTED => FindDevicesError::NotImplemented,
1723 AVERROR_BAD_FILE_DESCRIPTOR => FindDevicesError::BadFileDescriptor,
1724 _ => FindDevicesError::UnknownError(err_code),
1725 }
1726 }
1727}
1728
1729/// Errors from writing the output container header
1730/// (`avformat_write_header`), carried by
1731/// [`MuxingOperationError::WriteHeader`].
1732#[derive(thiserror::Error, Debug)]
1733#[non_exhaustive]
1734pub enum WriteHeaderError {
1735 /// Memory allocation failed (`AVERROR(ENOMEM)`).
1736 #[error("Memory allocation error")]
1737 OutOfMemory,
1738
1739 /// FFmpeg rejected an argument as invalid (`AVERROR(EINVAL)`).
1740 #[error("Invalid argument provided")]
1741 InvalidArgument,
1742
1743 /// The file or stream does not exist (`AVERROR(ENOENT)`).
1744 #[error("File or stream not found")]
1745 NotFound,
1746
1747 /// A low-level I/O error occurred (`AVERROR(EIO)`).
1748 #[error("I/O error occurred while writing the header")]
1749 IOError,
1750
1751 /// The stream or data connection was broken (`AVERROR(EPIPE)`).
1752 #[error("Pipe error, possibly the stream or data connection was broken")]
1753 PipeError,
1754
1755 /// An invalid file descriptor was used (`AVERROR(EBADF)`).
1756 #[error("Invalid file descriptor")]
1757 BadFileDescriptor,
1758
1759 /// The functionality or output format is not supported by the linked
1760 /// FFmpeg build (`AVERROR(ENOSYS)`).
1761 #[error("Functionality not implemented or unsupported output format")]
1762 NotImplemented,
1763
1764 /// The operation was not permitted (`AVERROR(EPERM)`).
1765 #[error("Operation not permitted to write the header")]
1766 OperationNotPermitted,
1767
1768 /// Permission was denied (`AVERROR(EACCES)`).
1769 #[error("Permission denied while writing the header")]
1770 PermissionDenied,
1771
1772 /// The operation timed out (`AVERROR(ETIMEDOUT)`).
1773 #[error("The connection timed out while trying to write the header")]
1774 Timeout,
1775
1776 /// Any other failure; the payload is the raw FFmpeg error code
1777 /// (rendered with `av_err2str` in the message).
1778 #[error("{}. ret:{0}", crate::util::ffmpeg_utils::av_err2str(*.0))]
1779 UnknownError(i32),
1780}
1781
1782impl From<i32> for WriteHeaderError {
1783 fn from(err_code: i32) -> Self {
1784 match err_code {
1785 AVERROR_OUT_OF_MEMORY => WriteHeaderError::OutOfMemory,
1786 AVERROR_INVALID_ARGUMENT => WriteHeaderError::InvalidArgument,
1787 AVERROR_NOT_FOUND => WriteHeaderError::NotFound,
1788 AVERROR_IO_ERROR => WriteHeaderError::IOError,
1789 AVERROR_PIPE_ERROR => WriteHeaderError::PipeError,
1790 AVERROR_BAD_FILE_DESCRIPTOR => WriteHeaderError::BadFileDescriptor,
1791 AVERROR_NOT_IMPLEMENTED => WriteHeaderError::NotImplemented,
1792 AVERROR_OPERATION_NOT_PERMITTED => WriteHeaderError::OperationNotPermitted,
1793 AVERROR_PERMISSION_DENIED => WriteHeaderError::PermissionDenied,
1794 AVERROR_TIMEOUT => WriteHeaderError::Timeout,
1795 _ => WriteHeaderError::UnknownError(err_code),
1796 }
1797 }
1798}
1799
1800/// Errors from encoding a subtitle (`avcodec_encode_subtitle`).
1801#[derive(thiserror::Error, Debug)]
1802#[non_exhaustive]
1803pub enum EncodeSubtitleError {
1804 /// Memory allocation failed (`AVERROR(ENOMEM)`).
1805 #[error("Memory allocation error while encoding subtitle")]
1806 OutOfMemory,
1807
1808 /// FFmpeg rejected an argument as invalid (`AVERROR(EINVAL)`).
1809 #[error("Invalid argument provided for subtitle encoding")]
1810 InvalidArgument,
1811
1812 /// The operation was not permitted (`AVERROR(EPERM)`).
1813 #[error("Operation not permitted while encoding subtitle")]
1814 OperationNotPermitted,
1815
1816 /// Subtitle encoding is not supported by the linked FFmpeg build
1817 /// (`AVERROR(ENOSYS)`).
1818 #[error("The encoding functionality is not implemented or unsupported")]
1819 NotImplemented,
1820
1821 /// The encoder is temporarily unable to accept input
1822 /// (`AVERROR(EAGAIN)`); retry later.
1823 #[error("Encoder temporarily unable to process, please retry")]
1824 TryAgain,
1825
1826 /// Any other failure; the payload is the raw FFmpeg error code.
1827 #[error("Subtitle encoding failed with unknown error. ret: {0}")]
1828 UnknownError(i32),
1829}
1830
1831impl From<i32> for EncodeSubtitleError {
1832 fn from(err_code: i32) -> Self {
1833 match err_code {
1834 AVERROR_OUT_OF_MEMORY => EncodeSubtitleError::OutOfMemory,
1835 AVERROR_INVALID_ARGUMENT => EncodeSubtitleError::InvalidArgument,
1836 AVERROR_OPERATION_NOT_PERMITTED => EncodeSubtitleError::OperationNotPermitted,
1837 AVERROR_NOT_IMPLEMENTED => EncodeSubtitleError::NotImplemented,
1838 AVERROR_AGAIN => EncodeSubtitleError::TryAgain,
1839 _ => EncodeSubtitleError::UnknownError(err_code),
1840 }
1841 }
1842}
1843
1844/// Errors from allocating an `AVPacket`.
1845#[derive(thiserror::Error, Debug)]
1846#[non_exhaustive]
1847pub enum AllocPacketError {
1848 /// Packet allocation failed (`av_packet_alloc` returned no packet).
1849 #[error("Memory allocation error while alloc packet")]
1850 OutOfMemory,
1851}
1852
1853/// Errors from allocating an `AVFrame`.
1854#[derive(thiserror::Error, Debug)]
1855#[non_exhaustive]
1856pub enum AllocFrameError {
1857 /// Frame allocation failed (`av_frame_alloc` returned no frame).
1858 #[error("Memory allocation error while alloc frame")]
1859 OutOfMemory,
1860}
1861
1862/// Errors from [`make_frame_writable`], the safe wrapper over FFmpeg's
1863/// `av_frame_make_writable`: ensuring exclusive ownership of a frame's data
1864/// buffers may allocate new buffers and copy into them, and that underlying
1865/// call can fail. Common AVERROR codes map to named variants; anything else
1866/// carries the raw code.
1867///
1868/// [`make_frame_writable`]: crate::util::ffmpeg_utils::make_frame_writable
1869#[derive(thiserror::Error, Debug)]
1870#[non_exhaustive]
1871pub enum FrameWritableError {
1872 /// Allocating or copying the frame's data buffers failed
1873 /// (`AVERROR(ENOMEM)`).
1874 #[error("Memory allocation error while copying frame data")]
1875 OutOfMemory,
1876
1877 /// FFmpeg rejected the frame as invalid (`AVERROR(EINVAL)`).
1878 #[error("Invalid argument provided")]
1879 InvalidArgument,
1880
1881 /// Any other failure; the payload is the raw FFmpeg error code
1882 /// (rendered with `av_err2str` in the message).
1883 #[error("{}. ret:{0}", crate::util::ffmpeg_utils::av_err2str(*.0))]
1884 UnknownError(i32),
1885}
1886
1887impl From<i32> for FrameWritableError {
1888 fn from(err_code: i32) -> Self {
1889 match err_code {
1890 AVERROR_OUT_OF_MEMORY => FrameWritableError::OutOfMemory,
1891 AVERROR_INVALID_ARGUMENT => FrameWritableError::InvalidArgument,
1892 _ => FrameWritableError::UnknownError(err_code),
1893 }
1894 }
1895}
1896
1897/// FFmpeg-level errors during muxing, carried by [`MuxingOperationError`].
1898#[derive(thiserror::Error, Debug)]
1899#[non_exhaustive]
1900pub enum MuxingError {
1901 /// Memory allocation failed (`AVERROR(ENOMEM)`).
1902 #[error("Memory allocation error")]
1903 OutOfMemory,
1904
1905 /// FFmpeg rejected an argument as invalid (`AVERROR(EINVAL)`).
1906 #[error("Invalid argument provided")]
1907 InvalidArgument,
1908
1909 /// A low-level I/O error occurred (`AVERROR(EIO)`).
1910 #[error("I/O error occurred during muxing")]
1911 IOError,
1912
1913 /// The stream or data connection was broken (`AVERROR(EPIPE)`).
1914 #[error("Broken pipe during muxing")]
1915 PipeError,
1916
1917 /// An invalid file descriptor was used (`AVERROR(EBADF)`).
1918 #[error("Bad file descriptor encountered")]
1919 BadFileDescriptor,
1920
1921 /// The functionality is not supported by the linked FFmpeg build
1922 /// (`AVERROR(ENOSYS)`).
1923 #[error("Functionality not implemented or unsupported")]
1924 NotImplemented,
1925
1926 /// The operation was not permitted (`AVERROR(EPERM)`).
1927 #[error("Operation not permitted")]
1928 OperationNotPermitted,
1929
1930 /// The resource is temporarily unavailable (`AVERROR(EAGAIN)`).
1931 #[error("Resource temporarily unavailable")]
1932 TryAgain,
1933
1934 /// Any other failure; the payload is the raw FFmpeg error code
1935 /// (rendered with `av_err2str` in the message).
1936 #[error("{}. ret:{0}", crate::util::ffmpeg_utils::av_err2str(*.0))]
1937 UnknownError(i32),
1938}
1939
1940impl From<i32> for MuxingError {
1941 fn from(err_code: i32) -> Self {
1942 match err_code {
1943 AVERROR_OUT_OF_MEMORY => MuxingError::OutOfMemory,
1944 AVERROR_INVALID_ARGUMENT => MuxingError::InvalidArgument,
1945 AVERROR_IO_ERROR => MuxingError::IOError,
1946 AVERROR_PIPE_ERROR => MuxingError::PipeError,
1947 AVERROR_BAD_FILE_DESCRIPTOR => MuxingError::BadFileDescriptor,
1948 AVERROR_NOT_IMPLEMENTED => MuxingError::NotImplemented,
1949 AVERROR_OPERATION_NOT_PERMITTED => MuxingError::OperationNotPermitted,
1950 AVERROR_AGAIN => MuxingError::TryAgain,
1951 _ => MuxingError::UnknownError(err_code),
1952 }
1953 }
1954}
1955
1956/// FFmpeg-level errors while opening an encoder, carried by
1957/// [`OpenEncoderOperationError`].
1958#[derive(thiserror::Error, Debug)]
1959#[non_exhaustive]
1960pub enum OpenEncoderError {
1961 /// Memory allocation failed (`AVERROR(ENOMEM)`).
1962 #[error("Memory allocation error occurred during encoder initialization")]
1963 OutOfMemory,
1964
1965 /// FFmpeg rejected an argument as invalid (`AVERROR(EINVAL)`).
1966 #[error("Invalid argument provided to encoder")]
1967 InvalidArgument,
1968
1969 /// A low-level I/O error occurred (`AVERROR(EIO)`).
1970 #[error("I/O error occurred while opening encoder")]
1971 IOError,
1972
1973 /// The stream or data connection was broken (`AVERROR(EPIPE)`).
1974 #[error("Broken pipe encountered during encoder initialization")]
1975 PipeError,
1976
1977 /// An invalid file descriptor was used (`AVERROR(EBADF)`).
1978 #[error("Bad file descriptor used in encoder")]
1979 BadFileDescriptor,
1980
1981 /// The functionality is not supported by the linked FFmpeg build
1982 /// (`AVERROR(ENOSYS)`).
1983 #[error("Encoder functionality not implemented or unsupported")]
1984 NotImplemented,
1985
1986 /// The operation was not permitted (`AVERROR(EPERM)`).
1987 #[error("Operation not permitted while configuring encoder")]
1988 OperationNotPermitted,
1989
1990 /// The resource is temporarily unavailable (`AVERROR(EAGAIN)`).
1991 #[error("Resource temporarily unavailable during encoder setup")]
1992 TryAgain,
1993
1994 /// Any other failure; the payload is the raw FFmpeg error code.
1995 #[error("An unknown error occurred in encoder setup. ret:{0}")]
1996 UnknownError(i32),
1997}
1998
1999impl From<i32> for OpenEncoderError {
2000 fn from(err_code: i32) -> Self {
2001 match err_code {
2002 AVERROR_OUT_OF_MEMORY => OpenEncoderError::OutOfMemory,
2003 AVERROR_INVALID_ARGUMENT => OpenEncoderError::InvalidArgument,
2004 AVERROR_IO_ERROR => OpenEncoderError::IOError,
2005 AVERROR_PIPE_ERROR => OpenEncoderError::PipeError,
2006 AVERROR_BAD_FILE_DESCRIPTOR => OpenEncoderError::BadFileDescriptor,
2007 AVERROR_NOT_IMPLEMENTED => OpenEncoderError::NotImplemented,
2008 AVERROR_OPERATION_NOT_PERMITTED => OpenEncoderError::OperationNotPermitted,
2009 AVERROR_AGAIN => OpenEncoderError::TryAgain,
2010 _ => OpenEncoderError::UnknownError(err_code),
2011 }
2012 }
2013}
2014
2015/// FFmpeg-level errors during encoding, carried by
2016/// [`EncodingOperationError`].
2017#[derive(thiserror::Error, Debug)]
2018#[non_exhaustive]
2019pub enum EncodingError {
2020 /// Memory allocation failed (`AVERROR(ENOMEM)`).
2021 #[error("Memory allocation error during encoding")]
2022 OutOfMemory,
2023
2024 /// FFmpeg rejected an argument as invalid (`AVERROR(EINVAL)`).
2025 #[error("Invalid argument provided to encoder")]
2026 InvalidArgument,
2027
2028 /// A low-level I/O error occurred (`AVERROR(EIO)`).
2029 #[error("I/O error occurred during encoding")]
2030 IOError,
2031
2032 /// The stream or data connection was broken (`AVERROR(EPIPE)`).
2033 #[error("Broken pipe encountered during encoding")]
2034 PipeError,
2035
2036 /// An invalid file descriptor was used (`AVERROR(EBADF)`).
2037 #[error("Bad file descriptor encountered during encoding")]
2038 BadFileDescriptor,
2039
2040 /// The functionality is not supported by the linked FFmpeg build
2041 /// (`AVERROR(ENOSYS)`).
2042 #[error("Functionality not implemented or unsupported encoding feature")]
2043 NotImplemented,
2044
2045 /// The operation was not permitted (`AVERROR(EPERM)`).
2046 #[error("Operation not permitted for encoder")]
2047 OperationNotPermitted,
2048
2049 /// The encoder is temporarily unable to accept or produce data
2050 /// (`AVERROR(EAGAIN)`).
2051 #[error("Resource temporarily unavailable, try again later")]
2052 TryAgain,
2053
2054 /// The encoder reached end of stream; no more packets will be produced
2055 /// (`AVERROR_EOF`).
2056 #[error("End of stream reached or no more frames to encode")]
2057 EndOfStream,
2058
2059 /// Any other failure; the payload is the raw FFmpeg error code.
2060 #[error("An unknown error occurred during encoding. ret: {0}")]
2061 UnknownError(i32),
2062}
2063
2064impl From<i32> for EncodingError {
2065 fn from(err_code: i32) -> Self {
2066 match err_code {
2067 AVERROR_OUT_OF_MEMORY => EncodingError::OutOfMemory,
2068 AVERROR_INVALID_ARGUMENT => EncodingError::InvalidArgument,
2069 AVERROR_IO_ERROR => EncodingError::IOError,
2070 AVERROR_PIPE_ERROR => EncodingError::PipeError,
2071 AVERROR_BAD_FILE_DESCRIPTOR => EncodingError::BadFileDescriptor,
2072 AVERROR_NOT_IMPLEMENTED => EncodingError::NotImplemented,
2073 AVERROR_OPERATION_NOT_PERMITTED => EncodingError::OperationNotPermitted,
2074 AVERROR_AGAIN => EncodingError::TryAgain,
2075 AVERROR_EOF => EncodingError::EndOfStream,
2076 _ => EncodingError::UnknownError(err_code),
2077 }
2078 }
2079}
2080
2081/// FFmpeg-level errors during filtergraph processing, carried by
2082/// [`FilterGraphOperationError`].
2083#[derive(thiserror::Error, Debug)]
2084#[non_exhaustive]
2085pub enum FilterGraphError {
2086 /// Memory allocation failed (`AVERROR(ENOMEM)`).
2087 #[error("Memory allocation error during filter graph processing")]
2088 OutOfMemory,
2089
2090 /// FFmpeg rejected an argument as invalid (`AVERROR(EINVAL)`).
2091 #[error("Invalid argument provided to filter graph processing")]
2092 InvalidArgument,
2093
2094 /// A low-level I/O error occurred (`AVERROR(EIO)`).
2095 #[error("I/O error occurred during filter graph processing")]
2096 IOError,
2097
2098 /// The stream or data connection was broken (`AVERROR(EPIPE)`).
2099 #[error("Broken pipe during filter graph processing")]
2100 PipeError,
2101
2102 /// An invalid file descriptor was used (`AVERROR(EBADF)`).
2103 #[error("Bad file descriptor encountered during filter graph processing")]
2104 BadFileDescriptor,
2105
2106 /// The functionality is not supported by the linked FFmpeg build
2107 /// (`AVERROR(ENOSYS)`).
2108 #[error("Functionality not implemented or unsupported during filter graph processing")]
2109 NotImplemented,
2110
2111 /// The operation was not permitted (`AVERROR(EPERM)`).
2112 #[error("Operation not permitted during filter graph processing")]
2113 OperationNotPermitted,
2114
2115 /// The graph is temporarily unable to accept or produce data
2116 /// (`AVERROR(EAGAIN)`).
2117 #[error("Resource temporarily unavailable during filter graph processing")]
2118 TryAgain,
2119
2120 /// The filtergraph reached end of stream (`AVERROR_EOF`).
2121 #[error("EOF")]
2122 EOF,
2123
2124 /// Any other failure; the payload is the raw FFmpeg error code.
2125 #[error("An unknown error occurred during filter graph processing. ret:{0}")]
2126 UnknownError(i32),
2127}
2128
2129impl From<i32> for FilterGraphError {
2130 fn from(err_code: i32) -> Self {
2131 match err_code {
2132 AVERROR_OUT_OF_MEMORY => FilterGraphError::OutOfMemory,
2133 AVERROR_INVALID_ARGUMENT => FilterGraphError::InvalidArgument,
2134 AVERROR_IO_ERROR => FilterGraphError::IOError,
2135 AVERROR_PIPE_ERROR => FilterGraphError::PipeError,
2136 AVERROR_BAD_FILE_DESCRIPTOR => FilterGraphError::BadFileDescriptor,
2137 AVERROR_NOT_IMPLEMENTED => FilterGraphError::NotImplemented,
2138 AVERROR_OPERATION_NOT_PERMITTED => FilterGraphError::OperationNotPermitted,
2139 AVERROR_AGAIN => FilterGraphError::TryAgain,
2140 AVERROR_EOF => FilterGraphError::EOF,
2141 _ => FilterGraphError::UnknownError(err_code),
2142 }
2143 }
2144}
2145
2146/// FFmpeg-level errors while opening a decoder, carried by
2147/// [`OpenDecoderOperationError`].
2148#[derive(thiserror::Error, Debug)]
2149#[non_exhaustive]
2150pub enum OpenDecoderError {
2151 /// Memory allocation failed (`AVERROR(ENOMEM)`).
2152 #[error("Memory allocation error during decoder initialization")]
2153 OutOfMemory,
2154
2155 /// FFmpeg rejected an argument as invalid (`AVERROR(EINVAL)`).
2156 #[error("Invalid argument provided during decoder initialization")]
2157 InvalidArgument,
2158
2159 /// The functionality is not supported by the linked FFmpeg build
2160 /// (`AVERROR(ENOSYS)`).
2161 #[error("Functionality not implemented or unsupported during decoder initialization")]
2162 NotImplemented,
2163
2164 /// The resource is temporarily unavailable (`AVERROR(EAGAIN)`).
2165 #[error("Resource temporarily unavailable during decoder initialization")]
2166 TryAgain,
2167
2168 /// A low-level I/O error occurred (`AVERROR(EIO)`).
2169 #[error("I/O error occurred during decoder initialization")]
2170 IOError,
2171
2172 /// Any other failure; the payload is the raw FFmpeg error code.
2173 #[error("An unknown error occurred during decoder initialization: {0}")]
2174 UnknownError(i32),
2175}
2176
2177impl From<i32> for OpenDecoderError {
2178 fn from(err_code: i32) -> Self {
2179 match err_code {
2180 AVERROR_OUT_OF_MEMORY => OpenDecoderError::OutOfMemory,
2181 AVERROR_INVALID_ARGUMENT => OpenDecoderError::InvalidArgument,
2182 AVERROR_NOT_IMPLEMENTED => OpenDecoderError::NotImplemented,
2183 AVERROR_AGAIN => OpenDecoderError::TryAgain,
2184 AVERROR_IO_ERROR => OpenDecoderError::IOError,
2185 _ => OpenDecoderError::UnknownError(err_code),
2186 }
2187 }
2188}
2189
2190/// FFmpeg-level errors during decoding, carried by
2191/// [`DecodingOperationError`].
2192#[derive(thiserror::Error, Debug)]
2193#[non_exhaustive]
2194pub enum DecodingError {
2195 /// Memory allocation failed (`AVERROR(ENOMEM)`).
2196 #[error("Memory allocation error")]
2197 OutOfMemory,
2198
2199 /// FFmpeg rejected an argument as invalid (`AVERROR(EINVAL)`).
2200 #[error("Invalid argument provided")]
2201 InvalidArgument,
2202
2203 /// A low-level I/O error occurred (`AVERROR(EIO)`).
2204 #[error("I/O error occurred during decoding")]
2205 IOError,
2206
2207 /// The operation timed out (`AVERROR(ETIMEDOUT)`).
2208 #[error("Timeout occurred during decoding")]
2209 Timeout,
2210
2211 /// The stream or data connection was broken (`AVERROR(EPIPE)`).
2212 #[error("Broken pipe encountered during decoding")]
2213 PipeError,
2214
2215 /// An invalid file descriptor was used (`AVERROR(EBADF)`).
2216 #[error("Bad file descriptor encountered during decoding")]
2217 BadFileDescriptor,
2218
2219 /// The functionality or format is not supported by the linked FFmpeg
2220 /// build (`AVERROR(ENOSYS)`).
2221 #[error("Unsupported functionality or format encountered")]
2222 NotImplemented,
2223
2224 /// The operation was not permitted (`AVERROR(EPERM)`).
2225 #[error("Operation not permitted")]
2226 OperationNotPermitted,
2227
2228 /// The decoder is temporarily unable to accept or produce data
2229 /// (`AVERROR(EAGAIN)`).
2230 #[error("Resource temporarily unavailable")]
2231 TryAgain,
2232
2233 /// Any other failure; the payload is the raw FFmpeg error code.
2234 #[error("An unknown decoding error occurred. ret:{0}")]
2235 UnknownError(i32),
2236}
2237
2238impl From<i32> for DecodingError {
2239 fn from(err_code: i32) -> Self {
2240 match err_code {
2241 AVERROR_OUT_OF_MEMORY => DecodingError::OutOfMemory,
2242 AVERROR_INVALID_ARGUMENT => DecodingError::InvalidArgument,
2243 AVERROR_IO_ERROR => DecodingError::IOError,
2244 AVERROR_TIMEOUT => DecodingError::Timeout,
2245 AVERROR_PIPE_ERROR => DecodingError::PipeError,
2246 AVERROR_BAD_FILE_DESCRIPTOR => DecodingError::BadFileDescriptor,
2247 AVERROR_NOT_IMPLEMENTED => DecodingError::NotImplemented,
2248 AVERROR_OPERATION_NOT_PERMITTED => DecodingError::OperationNotPermitted,
2249 AVERROR_AGAIN => DecodingError::TryAgain,
2250 _ => DecodingError::UnknownError(err_code),
2251 }
2252 }
2253}
2254
2255/// Errors from resolving a decoder.
2256#[derive(thiserror::Error, Debug)]
2257#[non_exhaustive]
2258pub enum DecoderError {
2259 /// Returned when a decoder requested by name is not provided by the
2260 /// linked FFmpeg build; the payload is the requested name.
2261 #[error("decoder '{0}' not found")]
2262 NotFound(String),
2263}
2264
2265/// FFmpeg-level errors during demuxing, carried by
2266/// [`DemuxingOperationError`] and [`PacketScannerError`].
2267#[derive(thiserror::Error, Debug)]
2268#[non_exhaustive]
2269pub enum DemuxingError {
2270 /// Memory allocation failed (`AVERROR(ENOMEM)`).
2271 #[error("Memory allocation error")]
2272 OutOfMemory,
2273
2274 /// FFmpeg rejected an argument as invalid (`AVERROR(EINVAL)`).
2275 #[error("Invalid argument provided")]
2276 InvalidArgument,
2277
2278 /// A low-level I/O error occurred (`AVERROR(EIO)`).
2279 #[error("I/O error occurred during demuxing")]
2280 IOError,
2281
2282 /// End of file was reached during demuxing (`AVERROR_EOF`).
2283 #[error("End of file reached during demuxing")]
2284 EndOfFile,
2285
2286 /// The resource is temporarily unavailable (`AVERROR(EAGAIN)`).
2287 #[error("Resource temporarily unavailable")]
2288 TryAgain,
2289
2290 /// The functionality is not supported by the linked FFmpeg build
2291 /// (`AVERROR(ENOSYS)`).
2292 #[error("Functionality not implemented or unsupported")]
2293 NotImplemented,
2294
2295 /// The operation was not permitted (`AVERROR(EPERM)`).
2296 #[error("Operation not permitted")]
2297 OperationNotPermitted,
2298
2299 /// An invalid file descriptor was used (`AVERROR(EBADF)`).
2300 #[error("Bad file descriptor encountered")]
2301 BadFileDescriptor,
2302
2303 /// The input contains invalid or corrupted data
2304 /// (`AVERROR_INVALIDDATA`).
2305 #[error("Invalid data found when processing input")]
2306 InvalidData,
2307
2308 /// Any other failure; the payload is the raw FFmpeg error code
2309 /// (rendered with `av_err2str` in the message).
2310 #[error("{}. ret:{0}", crate::util::ffmpeg_utils::av_err2str(*.0))]
2311 UnknownError(i32),
2312}
2313
2314impl From<i32> for DemuxingError {
2315 fn from(err_code: i32) -> Self {
2316 match err_code {
2317 AVERROR_OUT_OF_MEMORY => DemuxingError::OutOfMemory,
2318 AVERROR_INVALID_ARGUMENT => DemuxingError::InvalidArgument,
2319 AVERROR_IO_ERROR => DemuxingError::IOError,
2320 AVERROR_EOF => DemuxingError::EndOfFile,
2321 AVERROR_AGAIN => DemuxingError::TryAgain,
2322 AVERROR_NOT_IMPLEMENTED => DemuxingError::NotImplemented,
2323 AVERROR_OPERATION_NOT_PERMITTED => DemuxingError::OperationNotPermitted,
2324 AVERROR_BAD_FILE_DESCRIPTOR => DemuxingError::BadFileDescriptor,
2325 AVERROR_INVALIDDATA => DemuxingError::InvalidData,
2326 _ => DemuxingError::UnknownError(err_code),
2327 }
2328 }
2329}
2330
2331/// Errors that can occur during packet scanning operations.
2332#[derive(thiserror::Error, Debug)]
2333#[non_exhaustive]
2334pub enum PacketScannerError {
2335 /// Failed to seek to the requested timestamp.
2336 #[error("while seeking: {0}")]
2337 SeekError(DemuxingError),
2338
2339 /// Failed to read the next packet from the demuxer.
2340 #[error("while reading packet: {0}")]
2341 ReadError(DemuxingError),
2342}
2343
2344#[cfg(test)]
2345mod tests {
2346 // Regression: FrameSourceThreadExited is payload-less, but the manual
2347 // PartialEq whitelist omitted it, so the variant compared unequal to
2348 // itself — breaking the impl's documented "structural equality for
2349 // payload-less variants" contract.
2350 #[test]
2351 fn frame_source_thread_exited_equals_itself() {
2352 use super::Error;
2353 assert_eq!(
2354 Error::FrameSourceThreadExited,
2355 Error::FrameSourceThreadExited
2356 );
2357 assert_ne!(Error::FrameSourceThreadExited, Error::NotStarted);
2358 }
2359
2360 // Regression: FilterGraphParseError declares PermissionDenied and NotSocket,
2361 // but its From<i32> once omitted them, so an EACCES/ENOTSOCK filtergraph
2362 // error degraded to UnknownError and the two declared variants were
2363 // unreachable. Map the codes to the variants the enum already exposes.
2364 #[test]
2365 fn filter_graph_parse_error_maps_permission_and_socket_codes() {
2366 use super::{FilterGraphParseError, AVERROR_NOT_SOCKET, AVERROR_PERMISSION_DENIED};
2367 assert!(matches!(
2368 FilterGraphParseError::from(AVERROR_PERMISSION_DENIED),
2369 FilterGraphParseError::PermissionDenied
2370 ));
2371 assert!(matches!(
2372 FilterGraphParseError::from(AVERROR_NOT_SOCKET),
2373 FilterGraphParseError::NotSocket
2374 ));
2375 }
2376
2377 // make_frame_writable's failure is typed like every other AVERROR-coded
2378 // failure in this file: common codes map to named variants, the rest keep
2379 // the raw code. Pin the mapping and the user-facing Display string.
2380 #[test]
2381 fn frame_writable_error_maps_codes_and_pins_display() {
2382 use super::{Error, FrameWritableError, AVERROR_INVALID_ARGUMENT, AVERROR_OUT_OF_MEMORY};
2383 assert!(matches!(
2384 FrameWritableError::from(AVERROR_OUT_OF_MEMORY),
2385 FrameWritableError::OutOfMemory
2386 ));
2387 assert!(matches!(
2388 FrameWritableError::from(AVERROR_INVALID_ARGUMENT),
2389 FrameWritableError::InvalidArgument
2390 ));
2391 assert!(matches!(
2392 FrameWritableError::from(-99),
2393 FrameWritableError::UnknownError(-99)
2394 ));
2395 let err = Error::from(FrameWritableError::from(AVERROR_OUT_OF_MEMORY));
2396 assert_eq!(
2397 err.to_string(),
2398 "Frame writable error: Memory allocation error while copying frame data"
2399 );
2400 }
2401
2402 // The deprecated OpenGL filter's constructor failures are typed like the
2403 // wgpu successor's: they carry OpenGLFilterError and convert into
2404 // Error::OpenGLFilter. Pin the user-facing Display strings.
2405 #[cfg(feature = "opengl")]
2406 #[test]
2407 fn opengl_filter_error_pins_display() {
2408 use super::{Error, OpenGLFilterError};
2409 let err = Error::from(OpenGLFilterError::InvalidOption(
2410 "fragment shader must declare 'in vec2 TexCoord;'".to_string(),
2411 ));
2412 assert_eq!(
2413 err.to_string(),
2414 "OpenGL filter error: invalid OpenGL filter option: \
2415 fragment shader must declare 'in vec2 TexCoord;'"
2416 );
2417 let err = Error::from(OpenGLFilterError::ContextCreation(
2418 "Failed to create Surfman connection".to_string(),
2419 ));
2420 assert_eq!(
2421 err.to_string(),
2422 "OpenGL filter error: OpenGL context creation failed: \
2423 Failed to create Surfman connection"
2424 );
2425 }
2426}