Skip to main content

VideoDecoder

Struct VideoDecoder 

Source
pub struct VideoDecoder { /* private fields */ }
Expand description

Hardware-accelerated video decoder.

Hardware-only — there is no software fallback inside this crate. If every hardware backend in the platform’s probe order fails to open, open returns Error::AllBackendsFailed and the caller is responsible for falling back to a software decoder of their choice (e.g. ffmpeg::decoder::Video).

Mirrors ffmpeg::decoder::Video’s send_packet/receive_frame interface. Decoded frames are returned through crate::Frame, a CPU-side wrapper whose accessors avoid the AVPixelFormat-enum UB that an unvalidated read of FFmpeg’s raw integer pixel formats can trigger.

open does a true probe: each backend opens with a strict get_format callback. On the first non-transient error from a backend the decoder is torn down and the next backend in probe order is tried, with all packets seen so far replayed through it. The advance is transactional — the candidate backend must successfully build and accept the replayed packets before any probe state is consumed, so a failing backend in the middle of the order does not strand the caller without history. Once the first frame is delivered the probe collapses and subsequent calls go straight to the active backend.

Implementations§

Source§

impl VideoDecoder

Source

pub fn open(parameters: Parameters) -> Result<Self>

Auto-probe hardware backends in the platform’s default order.

Each backend opens with a strict get_format callback. The first backend whose avcodec_open2 succeeds becomes active; if its first frame is unusable (decode error, transfer failure, or a CPU-format frame from a HW context) the decoder is torn down and the next backend is tried — packets sent so far are replayed through the new decoder transparently. The probe advance is transactional: the next backend must build and accept the replayed history before any probe state is consumed, so a misbehaving middle backend cannot strand the caller.

Self::backend reflects whichever backend ultimately produced the first frame.

Error::AllBackendsFailed surfaces in two places, with the same meaning (“no hardware backend can decode this stream — fall back to software yourself”):

  • From open itself, when no backend even opens.
  • From Self::send_packet / Self::send_eof / Self::receive_frame, when the initially-opened backend fails at decode time and every remaining backend in the probe order either also fails or doesn’t exist. On single-backend platforms (e.g. macOS, where the order is [VideoToolbox]), this is the only place a HW-only failure surfaces.

In both cases, attempts carries the per-backend error log. When the runtime path fires, unconsumed_packets also contains the packets the decoder consumed from the caller before the probe exhausted (refcounted shallow clones); for non-seekable inputs (live streams, pipes) the caller can replay these directly into a software decoder of their choice without re-demuxing. From the open-time path the vec is empty since no packets have been sent.

On Ok, the returned decoder always has an active probe rescue safety net. If a parameters clone fails under memory pressure before the probe state can be set up, open returns Err(Error::Ffmpeg(Other { errno: ENOMEM })) rather than handing back a live decoder with no fallback contract. No packets have been sent yet, so the caller can retry or fall back to software with the original parameters directly.

Source

pub fn open_with(parameters: Parameters, backend: Backend) -> Result<Self>

Open the decoder with a specific backend. No probe, no fallback.

If backend cannot actually decode this stream, the failure surfaces from Self::receive_frame (the strict get_format callback returns AV_PIX_FMT_NONE, the decoder errors out). The caller is responsible for retrying with another hardware backend or falling back to a software decoder of their choice (e.g. ffmpeg::decoder::Video).

Source

pub fn with_max_probe_pending_bytes(self, bytes: usize) -> Self

Override the byte budget for probe-replay queued frames. Defaults to [DEFAULT_MAX_PROBE_PENDING_BYTES]. Use a higher value when targeting 8K+ workloads where 16 frames at full size could exceed the default; use a lower value in memory-constrained services to bound peak allocation more tightly.

Setting after the first frame has been delivered is harmless but has no observable effect — the probe has already collapsed and the cap only applies during replay drain.

Returns self for builder-style chaining:

let decoder = VideoDecoder::open(params)?
    .with_max_probe_pending_bytes(1024 * 1024 * 1024); // 1 GiB
Source

pub fn backend(&self) -> Backend

The backend currently producing frames. While the probe is still in progress (no frame received yet) this returns the optimistically selected backend; after the first frame, it is the backend that actually produced it. Once stable, never changes again.

Source

pub fn width(&self) -> u32

Decoder width in pixels.

Source

pub fn height(&self) -> u32

Decoder height in pixels.

Source

pub fn time_base(&self) -> Rational

Codec context time base.

Source

pub fn frame_rate(&self) -> Option<Rational>

Frame rate from the codec context, if known.

Source

pub fn send_packet(&mut self, packet: &Packet) -> Result<()>

Submit a packet to the decoder.

On success — and only on success — the packet is buffered for potential replay through a fallback backend while the probe is active. EAGAIN (decoder needs receive_frame to drain output first) propagates as normal backpressure; the caller drains then retries.

While the probe is active, a non-transient error (e.g. the active HW backend rejecting this stream’s geometry on first packet) advances the probe to the next candidate and retries the packet there. The caller observes only the eventual success or, if the probe is exhausted, the final error.

Atomic probe rescue. While the probe is active, the rescue invariant is that everything FFmpeg has consumed since open is reflected in buffered_packets (so a future Error::AllBackendsFailed can hand a complete replay history back to the caller for software fallback on a non-seekable input). If we cannot prove this packet is buffer-able — its side-data entry count exceeds [MAX_PROBE_PACKET_SIDE_DATA_ENTRIES], its bytes would push the probe past [MAX_PROBE_PACKETS] or [MAX_PROBE_PACKET_BYTES], or av_packet_ref fails ENOMEM — send_packet returns Error::AllBackendsFailed without invoking state.inner.send_packet on this packet. The caller’s packet stays in their hand and unconsumed_packets carries the pre-existing buffered history, so they can replay unconsumed_packets plus the current packet through their software decoder of choice. The post-probe path (after the first frame, when self.probe is None) skips this pre-flight entirely.

Source

pub fn send_eof(&mut self) -> Result<()>

Signal end-of-stream to the decoder.

Recorded for replay only if the underlying send_eof succeeds. While the probe is active, non-transient errors trigger probe advance and retry, matching send_packet’s behaviour.

Source

pub fn receive_frame(&mut self, frame: &mut Frame) -> Result<()>

Receive a CPU-side decoded frame.

The frame is downloaded with av_hwframe_transfer_data and metadata is copied via av_frame_copy_props. The caller’s frame is always unref’d first, so reuse across resolution changes or different decoders is safe.

While the probe window is open, any non-transient failure (decode error, transfer error, copy_props error, or a CPU-format frame from a HW-opened context) tears down the current decoder and advances to the next hardware backend in probe order, replaying buffered packets through it. Frames the candidate produced during replay (drained when send_packet returned EAGAIN) are queued and delivered FIFO via this method, so the caller never loses initial frames after a fallback.

This crate is hardware-only: there is no software fallback inside the decoder. When every backend in the probe order has been exhausted — including the case of a single-backend platform whose only backend failed — this returns Error::AllBackendsFailed with the per- backend attempt log so the caller can branch into a software decoder of their choice.

Returns the same transient signals as ffmpeg::decoder::Video: Error::Ffmpeg(Other { errno: EAGAIN }) when no frame is ready and more packets must be sent, and Error::Ffmpeg(Eof) once fully drained.

Source

pub fn flush(&mut self)

Flush internal buffers (e.g. after a seek).

Discards every frame buffered by the decoder, every frame queued during probe replay (pending_frames), and the residual hw_frame scratch buffer. Probe-time replay state (buffered packets, EOF marker) is also cleared since post-seek packets do not align with the previously captured history. After a flush, the next receive_frame waits for new post-seek input.

Trait Implementations§

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more