pub struct VaapiEncoder { /* private fields */ }Expand description
Hardware-accelerated H.264 encoder built on FFmpeg’s h264_vaapi, owning the whole
VA-API pipeline for one capture: device contexts, the surface pool, the color-convert
filter graph, the reusable frames/packet, and live rate-control state.
The pointer members are raw FFmpeg objects freed in Drop. Three groups matter:
- Device / frames contexts:
drm_device_ctx→ derivedhw_device_ctx;drm_frames_ctxdescribes the incoming DMA-BUF,enc_frames_ctxthe VA-surface pool the encoder draws from.enc_frames_ctxis kept referenced soreopen_codeccan rebuild the codec against the same pool. - Filter graph:
buffersrc_ctx→ hwmap/hwupload +scale_vaapi→buffersink_ctx, which lands every input on a GPU surface insw_format. - Reusable frames:
video_framefeeds the graph on the dmabuf/host paths,sw_frame+hw_framestage the direct planar upload inencode_raw, andpacketis the shared output.
sw_format is the surface format the session negotiated — NV12 for 4:2:0, or the 4:4:4 format
the driver offered — and every path keys its plane layout off it; packed_444 is the reused
repack scratch for a driver whose only 4:4:4 surface is packed.
current_qp / qp_hysteresis_counter drive the CQP hysteresis in update_qp. cbr_mode,
current_bitrate_kbps, current_vbv_mult, and current_kf_s cache the live rate-control state
so reconfigure_rate re-opens the codec only when a value actually changes.
omit_stripe_headers drops the 10-byte framing when the consumer wants a bare Annex-B stream.
Implementations§
Source§impl VaapiEncoder
impl VaapiEncoder
Sourcepub fn new(settings: &RustCaptureSettings) -> Result<Self, String>
pub fn new(settings: &RustCaptureSettings) -> Result<Self, String>
Build a VA-API encoder for the Wayland dmabuf path — the source is a DRM-PRIME
dmabuf that the filter graph hwmaps onto a VA surface. Thin wrapper over new_impl with
host_input = false.
Sourcepub fn new_host(settings: &RustCaptureSettings) -> Result<Self, String>
pub fn new_host(settings: &RustCaptureSettings) -> Result<Self, String>
Build a VA-API encoder for the X11 host-ARGB path — the source is a CPU BGRA frame
that the filter graph hwuploads onto a VA surface. Thin wrapper over new_impl with
host_input = true; the GPU still does the ARGB→YUV convert, so there is no CPU colorspace
conversion.
Sourcepub fn is_fullcolor(&self) -> bool
pub fn is_fullcolor(&self) -> bool
Whether this session negotiated 4:4:4 chroma. The request alone does not settle it — the driver and the FFmpeg build both have to carry it — so callers describing the active colorspace ask the encoder rather than the settings.
Sourcepub fn reconfigure_rate(
&mut self,
settings: &RustCaptureSettings,
) -> Result<(), String>
pub fn reconfigure_rate( &mut self, settings: &RustCaptureSettings, ) -> Result<(), String>
Stays cheap enough for the pipeline to call on every single frame by re-opening the codec only when a rate-control or framerate setting has actually changed — an unconditional re-open here would force a needless IDR every frame and cripple the stream.
The change test is deliberately narrow, to keep that guard tight: in CBR, a different target
bitrate or VBV multiplier; in any mode, a different target fps. When nothing changed it returns
without touching the codec. On a real change it caches the new fps / bitrate / VBV / keyframe
interval and calls reopen_codec carrying the current QP, so a CQP stream keeps its
quantizer across the change. Err means the re-open failed and the session no longer
has a codec context: the caller has to rebuild it.
Sourcepub fn encode_dmabuf(
&mut self,
dmabuf: &Dmabuf,
frame_number: u64,
qp: u32,
force_idr: bool,
) -> Result<Vec<u8>, String>
pub fn encode_dmabuf( &mut self, dmabuf: &Dmabuf, frame_number: u64, qp: u32, force_idr: bool, ) -> Result<Vec<u8>, String>
Encode one Wayland DRM-PRIME dmabuf by wrapping it in an FFmpeg DRM frame descriptor
and pushing it through the filter graph, which hwmaps it to a VA surface and converts to
the session’s surface format before encode.
- Quantizer: apply the requested
qpthroughupdate_qp(hysteresis / CBR-aware). - Descriptor: allocate a zeroed
AVDRMFrameDescriptorand populate it from the dmabuf — one object per handle with a freshlydup’d fd (so FFmpeg owns independent fds), the object size the fd reports (lseekto its end, which is the buffer object’s allocation whatever the tiling or padding), the format modifier, and one layer whose planes carry each plane’s offset and pitch. A single-handle multi-plane buffer points all planes at object 0. - Ownership: the dup’d fds live in a boxed
DmabufResourceshanded toav_buffer_createasrelease_drm_frame’s opaque, so FFmpeg closes them on teardown. If building that buffer fails,release_drm_frameis called directly to clean up. - Submit: point
video_frameat the descriptor, tag it DRM-PRIME with the DRM frames context, and feed the graph.av_buffersrc_add_frameconsumes the frame’s refs only on success; on error the frame is untouched, sovideo_frameis unref’d to releasebuf[0](which runsrelease_drm_frameand closes the fds) — no manual fd close, which would double-close. - Collect: pull each converted frame from the sink, stamp
pict_type = Iwhenforce_idr, send it to the encoder, and drain packets viacollect_packet.
Sourcepub fn encode_host_argb(
&mut self,
bgra: &[u8],
stride: usize,
frame_number: u64,
qp: u32,
force_idr: bool,
) -> Result<Vec<u8>, String>
pub fn encode_host_argb( &mut self, bgra: &[u8], stride: usize, frame_number: u64, qp: u32, force_idr: bool, ) -> Result<Vec<u8>, String>
Encode one host BGRA frame (X11 path) by staging it onto a VA surface and letting the
GPU do the color convert — valid only on an encoder built with new_host.
bgra is B,G,R,A in memory at stride bytes per row (padding allowed) and must hold at least
stride * height bytes. The flow: apply qp via update_qp; allocate a fresh refcounted
BGRA video_frame and copy the host rows in (the sole copy — plain data movement to a
GPU-uploadable frame, clamped to min(width*4, stride, dst_stride) per row, not a
colorspace conversion); feed the graph, where hwupload stages it onto a VA surface and
scale_vaapi converts ARGB→YUV; then pull converted frames, stamp pict_type = I when
force_idr, encode, and drain packets via collect_packet.
Sourcepub fn encode_raw(
&mut self,
pixels: &[u8],
frame_number: u64,
qp: u32,
force_idr: bool,
) -> Result<Vec<u8>, String>
pub fn encode_raw( &mut self, pixels: &[u8], frame_number: u64, qp: u32, force_idr: bool, ) -> Result<Vec<u8>, String>
Encode already-planar pixels by uploading them straight to a VA surface, bypassing the filter graph (there is no color convert to do).
pixels carries whichever layout the session negotiated: NV12 (w*h of Y then w*h/2 of
interleaved UV) for a 4:2:0 session, or planar I444 (w*h each of Y, U, V) for a 4:4:4 one.
The sw_frame is pointed at those planes without copying, except on a driver whose only
4:4:4 surface is packed, where pack_i444_as_vuyx stages the frame into a reused buffer
first. A fresh hw_frame is pulled from the encoder pool (the frame is unref’d first, or the
prior surface would leak), av_hwframe_transfer_data copies CPU→GPU, and the sw_frame is
released. Keyframes are forced through pict_type = I (AV_PKT_FLAG_KEY is a packet flag,
unusable on a frame); otherwise pict_type = NONE. The GPU frame is sent to the encoder and
packets are drained via collect_packet.
Trait Implementations§
Source§impl Drop for VaapiEncoder
Tear down every FFmpeg object in dependency order so nothing is freed while still
referenced: first the reusable packet and frames, then the filter graph and codec context, and
last the frames/device contexts they pointed at (encoder pool, DRM frames, VA-API device, DRM
device). Each pointer is null-checked so a partially-built encoder unwinds cleanly.
impl Drop for VaapiEncoder
Tear down every FFmpeg object in dependency order so nothing is freed while still referenced: first the reusable packet and frames, then the filter graph and codec context, and last the frames/device contexts they pointed at (encoder pool, DRM frames, VA-API device, DRM device). Each pointer is null-checked so a partially-built encoder unwinds cleanly.
impl Send for VaapiEncoder
Assert VaapiEncoder is Send: its raw FFmpeg pointers are owned exclusively and the
encoder is driven from a single capture thread, so moving the whole object across threads adds
no aliasing.
Auto Trait Implementations§
impl !Sync for VaapiEncoder
impl Freeze for VaapiEncoder
impl RefUnwindSafe for VaapiEncoder
impl Unpin for VaapiEncoder
impl UnsafeUnpin for VaapiEncoder
impl UnwindSafe for VaapiEncoder
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more