pub struct Sink(/* private fields */);Expand description
An Encoder confined to one thread, driven from anywhere.
Same shape as Encoder, one method at a time, except that
the calls are async and encode takes the frame by value
(it may cross a thread). Reach for this instead of an Encoder whenever the
encoder outlives a single thread’s stack: an object shared across threads, an
FFI handle, a task that migrates between executor workers. An Encoder you
build, drive, and drop inside one function needs none of it.
Awaiting rather than blocking is the point: the codec runs on its own thread, so the executor keeps its worker while a slow hardware encoder works through a frame. A caller with no executor to yield to (an FFI boundary that must return a result synchronously) blocks on these futures itself.
§Cancellation
These futures are not cancel-safe, and the sink says so rather than letting
it slide. The codec runs on its own thread, so a request that has been queued
runs whether or not anyone is still waiting: dropping the future (racing it in
a select!, giving it a timeout) leaves the codec a step ahead of the stream,
holding output nobody received. Rather than let the next call carry on and
publish a track quietly missing those frames, the sink refuses every call
after a cancelled one. Drop it and open another.
Racing an encode against a shutdown signal is fine, since the sink is on its way out anyway. What does not work is cancelling one and carrying on.
macOS never refuses, because there is no thread to run ahead: the encoder runs inline, so a dropped future either had not started the call or had already finished it. Write to the contract above regardless, or the same code loses frames off macOS.
Implementations§
Source§impl Sink
impl Sink
Sourcepub async fn open(config: &Config) -> Result<Self, Error>
pub async fn open(config: &Config) -> Result<Self, Error>
Open an encoder for config on its own thread. Returns once the encoder
is built (or its construction fails), so a bad config or a missing backend
surfaces here rather than on the first frame.
Sourcepub fn keyframe(&mut self)
pub fn keyframe(&mut self)
Ask for the next frame to be encoded as a keyframe, like
Encoder::keyframe.
Queued behind the frames already in flight rather than applied to
whichever one the codec happens to be on, so it keys the next frame you
pass to encode. Only queues the request, so unlike the
rest there is nothing to await.
Sourcepub async fn encode(
&mut self,
frame: impl Into<Arc<Frame>>,
) -> Result<Vec<Encoded>, Error>
pub async fn encode( &mut self, frame: impl Into<Arc<Frame>>, ) -> Result<Vec<Encoded>, Error>
Encode one frame, waiting for its access units.
Otherwise Encoder::encode: zero or more access
units, each stamped with the frame it came from.
Takes ownership, since the frame may be moved to the encode thread, but
takes it as anything that can become an Arc so a caller fanning one
frame out to several encoders (a transcode ladder) hands over a clone of
the handle rather than a copy of the pixels. Pass a Frame and it is
wrapped for you.
Sourcepub async fn set_bitrate(&mut self, bitrate: u64) -> Result<(), Error>
pub async fn set_bitrate(&mut self, bitrate: u64) -> Result<(), Error>
Retune the encoder, waiting for the backend’s verdict. See
Encoder::set_bitrate for what a failure
means (not fatal: stop adapting, keep encoding).
Sourcepub async fn flush(&mut self) -> Result<Vec<Encoded>, Error>
pub async fn flush(&mut self) -> Result<Vec<Encoded>, Error>
Empty the codec at a boundary the output has to respect, leaving it ready
for the frames that follow. See Encoder::flush.
A live track needs this at every group boundary: a backend that pipelines is still holding the last frames of a group when it ends, and they would otherwise surface in the next group ahead of its keyframe, where a subscriber joining there cannot decode them. Publishing frame by frame with no group structure needs none of it.
Sourcepub async fn finish(self) -> Result<Vec<Encoded>, Error>
pub async fn finish(self) -> Result<Vec<Encoded>, Error>
Drain the codec, returning every access unit it was still holding, and shut the encoder down.
Consumes the sink, like Encoder::finish.
Dropping a sink without this is fine and tears down just as cleanly, it
just discards the tail: publish the returned frames before ending a track,
or its last pictures never reach a subscriber.