pub struct VideoWriter { /* private fields */ }Expand description
Pushes raw video frames from Rust code into a full FFmpeg pipeline. See the
module documentation for the frame contract and teardown semantics.
Output files are finalized ONLY by finish; dropping the
writer aborts the run and leaves the output unfinalized.
Experimental: new in 0.14; the surface may still be refined. The constant-frame-rate, video-only contract is permanent (see the module docs).
Implementations§
Source§impl VideoWriter
impl VideoWriter
Sourcepub fn builder(width: u32, height: u32) -> VideoWriterBuilder
pub fn builder(width: u32, height: u32) -> VideoWriterBuilder
Starts a builder. Width and height are positional so they cannot be forgotten.
Sourcepub fn frame_size(&self) -> usize
pub fn frame_size(&self) -> usize
Exact number of bytes one frame must contain:
av_image_get_buffer_size(pix_fmt, width, height, 1) (tight packing).
Sourcepub fn write(&mut self, frame: &[u8]) -> Result<(), PushError>
pub fn write(&mut self, frame: &[u8]) -> Result<(), PushError>
Pushes one frame, copying the borrowed slice into an owned buffer. Blocks while the internal queue is full (backpressure).
Sourcepub fn write_owned(&mut self, frame: Vec<u8>) -> Result<(), OwnedPushError>
pub fn write_owned(&mut self, frame: Vec<u8>) -> Result<(), OwnedPushError>
Pushes one owned frame; the Vec is moved into the pipeline, saving
the borrow-copy that write performs. The pipeline
still copies the bytes once, plane-by-plane, into an aligned AVFrame
on the worker thread. frame.len() must equal
frame_size; the Vec is queued as-is, so any
spare capacity beyond its length stays allocated while it waits.
§Errors
Every error path hands the frame back inside OwnedPushError (the
SendError convention): the exact allocation the caller passed in,
recoverable via OwnedPushError::into_frame /
OwnedPushError::into_parts for reuse. This covers the size check
and a pipeline observed closed before, during, or after the queue
wait. Note that ? in a function returning crate::error::Result
converts away the payload — destructure first when the buffer matters.
Sourcepub fn finish(self) -> Result<()>
pub fn finish(self) -> Result<()>
Closes ingress (the frame source emits its end-of-stream marker),
drains the encoder, finalizes the container, and returns the first
authoritative pipeline error. This is the only graceful path: a
writer that is dropped instead is aborted and its output is not
finalized. A write that returned PushError::PipelineClosed
resolves here, either to the real cause or to Ok when the pipeline
closed after completing normally (e.g. an Output frame limit was
reached).
finish waits for the graph to drain, so a
filter_desc built to outlive the
pushed stream (an overlay without shortest=1, a concat onto an
unbounded generator) keeps it blocked until the generator ends — the
declared semantics, not a malfunction. Drop such a job (or call
abort) to discard it in bounded time instead — a
bound against remaining graph data; a user write/seek callback that
never returns holds finish and that teardown alike (see Drop).
Sourcepub fn abort(self)
pub fn abort(self)
Discards the export: closes ingress, then hard-aborts the scheduler and joins its workers. For “user cancelled” flows where the output is not wanted. Equivalent to dropping the writer — this method only makes the cancellation read as intent instead of a scope end.
The join is bounded by the workers’ status polls, not by remaining
data, but the abort may skip the encoder flush / muxer trailer, so the
partial file is not guaranteed playable. Use finish
to finalize. The one thing the join cannot bound is a user write/seek
callback: it runs synchronously on the mux worker with no status
check, so a callback that never returns blocks the join exactly as it
would block finish().
Trait Implementations§
Source§impl Drop for VideoWriter
impl Drop for VideoWriter
Source§fn drop(&mut self)
fn drop(&mut self)
Aborts the run — it does NOT finish it. Ingress is closed first (so
the frame-source worker observes the disconnect), then the scheduler
is hard-aborted and joined: bounded by the workers’ status polls,
never by how much data filters, the encoder, or an embedded generator
could still produce — the same contract as dropping a frame-export
iterator. The output is NOT finalized (no encoder drain, no muxer
trailer); finish is the only path that
finalizes. After finish/abort consumed the scheduler this is a
no-op. That bound does not reach a user write/seek callback, which
runs synchronously on the mux worker with no status check and, if it
never returns, holds up this join exactly as it would finish().