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
impl VideoDecoder
Sourcepub fn open(parameters: Parameters) -> Result<Self>
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
openitself, 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.
Sourcepub fn open_with(parameters: Parameters, backend: Backend) -> Result<Self>
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).
Sourcepub fn with_max_probe_pending_bytes(self, bytes: usize) -> Self
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 GiBSourcepub fn backend(&self) -> Backend
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.
Sourcepub fn frame_rate(&self) -> Option<Rational>
pub fn frame_rate(&self) -> Option<Rational>
Frame rate from the codec context, if known.
Sourcepub fn send_packet(&mut self, packet: &Packet) -> Result<()>
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.
Sourcepub fn send_eof(&mut self) -> Result<()>
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.
Sourcepub fn receive_frame(&mut self, frame: &mut Frame) -> Result<()>
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.
Sourcepub fn flush(&mut self)
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.