Skip to main content

FramePool

Struct FramePool 

Source
struct FramePool {
    inner: Mutex<PoolInner>,
    cv: Condvar,
    stop: AtomicBool,
    generation: AtomicU64,
}
Expand description

Demand-driven capture -> encode handoff: a bounded single-slot channel over a fixed set of pooled shm surfaces.

The capture thread writes into a pooled surface and publishes it into the single slot; the encode thread takes it. Capture stays at most one frame ahead of encode because acquire and publish BLOCK (bounded) until the encoder frees a surface / drains the slot, throttling capture to the encode rate. Since X11 capture is pull-based (no backlog), throttling — rather than capturing-then-dropping — means a full-resolution shm round-trip is never spent on a frame that would be discarded, while the next capture still overlaps the current encode (the throughput win). The encoder therefore only ever sees a contiguous frame stream, keeping the H.264 reference chain valid.

An x264 session encoding a surface’s picture in place reads it through the next encode as well, so the encode thread keeps that surface (held) until an encode no longer reads it: of the POOL_N surfaces one is then held, one encoded, and the third left for the capture to convert the next frame into while the encode runs, the capture still one frame ahead.

generation is bumped by the capture thread each time the backing shm surfaces are destroyed and recreated. Published frames carry it so the encode thread rebuilds its pipeline — dropping encoder state keyed to surface base pointers (e.g. NVENC’s pinned-host registrations) — even when a resize flap lands back on the old dimensions and the recreated segments reuse the old virtual addresses.

Fields§

§inner: Mutex<PoolInner>§cv: Condvar§stop: AtomicBool§generation: AtomicU64

Implementations§

Source§

impl FramePool

Source

fn new(n: usize) -> Self

Create a pool with n free surfaces, an empty handoff slot, and generation 0.

Source

fn bump_generation(&self)

Capture: record that the surfaces were recreated by advancing the generation. Only called after drain_for_resize succeeded, so no frame from the previous generation is still in flight.

Source

fn generation(&self) -> u64

Read the current surface generation, stamped onto each published frame.

Source

fn acquire(&self, stop: &AtomicBool) -> Option<usize>

Capture: claim a free surface to write the next frame into, or None on stop.

Blocks (bounded, re-checking stop every 20ms) until a surface is free, which throttles capture to the encode rate so a full-resolution capture is never spent on a frame that would be dropped. The bounded re-check means a stop that races the wait cannot hang the thread.

Source

fn publish(&self, frame: RawFrame, stop: &AtomicBool) -> bool

Capture: publish the just-captured frame into the single slot, or false (frame discarded) on stop.

Blocks (bounded, re-checking stop every 20ms) until the encode thread has taken the previous frame, so capture stays at most one frame ahead and never drops under normal flow.

Source

fn take(&self, release: impl FnMut()) -> Option<RawFrame>

Encode: block until a frame is available (Some) or stop is signaled (None).

The wait is bounded (re-checking stop every 20ms) as defense-in-depth against a lost wakeup, so a stop that races the park can never leave the encode thread blocked forever. A drain waiting for the held surface is answered here: release stops the encoder reading it, with the lock dropped, and the surface returns to the free list.

Source

fn recycle(&self, idx: usize)

Encode: return a surface to the free list after it has been encoded, waking any waiter parked in acquire / publish / drain_for_resize.

Source

fn settle(&self, back: &[usize], held: Option<usize>)

Encode: after a frame’s encode, return the surfaces back to the free list and keep held, the one whose picture the encoder reads through its next encode, out of it.

Source

fn drain_for_resize(&self, n: usize, stop: &AtomicBool) -> bool

Capture: before recreating surfaces (auto-adjust / region resize), reclaim the pending slot and wait until every surface is back in the free list, so no surface is destroyed while the encode thread is still reading it.

Reclaiming the slot returns any un-taken frame to the free list; the loop then waits until free.len() == n, i.e. the encode thread has finished any in-flight frame and, asked through release, returned the surface its encoder held. The wait is bounded and also breaks on stop — pool shutdown OR the external stop — so a panicked or dead encode thread that never recycles cannot wedge the capture thread here forever; a requested stop unblocks it. Returns true if it fully drained (safe to recreate surfaces), or false if it aborted on stop — in which case the caller tears down, which joins the encode thread before destroying surfaces, so the resize-safety guarantee still holds.

Source

fn shutdown(&self)

Signal every waiter to stop and wake them, ending the encode thread’s take loop.

The inner mutex is acquired BEFORE storing stop and notifying, which closes the lost-wakeup window: with the lock held, take is either still before its stop check or already parked on the condvar (and will receive the notify), never in the gap between the two. The notify is issued after the guard is dropped so the woken thread does not immediately re-block on the lock this call holds.

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
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<T> Downcast for T
where T: Any,

§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert 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>

Convert 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)

Convert &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)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Send + Sync> ⓘ

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

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

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

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> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts 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
§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

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.
§

impl<T> WithSubscriber for T

§

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
§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

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