pub struct Engine { /* private fields */ }Expand description
One embedded mpv player core.
Toolkit-agnostic by construction: no main-loop integration, no widget,
no GL context of its own. A shell attaches a render target with
attach_gl_render, forwards the update
callback to its own main loop, and drains
pump_events on whatever cadence suits it.
Implementations§
Source§impl Engine
impl Engine
Sourcepub fn builder() -> EngineBuilder
pub fn builder() -> EngineBuilder
Sourcepub fn video() -> EngineBuilder
pub fn video() -> EngineBuilder
Builder preset for video playback via the libmpv render API.
Frames appear only after attach_gl_render;
see that method’s note on load ordering.
Sourcepub fn headless() -> EngineBuilder
pub fn headless() -> EngineBuilder
Builder preset for audio-only / headless use (vo=null): playback
starts as soon as load runs, no render target needed.
Also what the test suite uses — no display required.
Sourcepub fn load(&self, source: &str) -> Result<()>
pub fn load(&self, source: &str) -> Result<()>
Load a file path or URL and start playback.
For video engines, prefer load_when_ready
until the shell’s surface is mapped: loadfile before a render
context exists fails VO init and drops the video track (see
load_when_ready’s docs for the full failure). Loading paused
doesn’t dodge it — the load itself is what fails — which is why
the deferred variant exists and why
load_paused is no pre-attach alternative.
Sourcepub fn load_paused(&self, source: &str) -> Result<()>
pub fn load_paused(&self, source: &str) -> Result<()>
load, but paused: pause is set before loadfile
so demuxing/audio don’t start before the shell is ready. Call
set_paused(false) on your window-ready
signal. (Setting pause at init time instead has a tendency to
hang — this runtime-property ordering is the reliable variant.)
Sourcepub fn load_when_ready(&self, source: &str) -> Result<()>
pub fn load_when_ready(&self, source: &str) -> Result<()>
load, deferred until frames have somewhere to go:
on a render-API engine (vo=libmpv, Engine::video) with no
context attached yet, the source is queued and the attach call
(attach_gl_render /
attach_sw_render) issues the
loadfile — the ordering a video shell wants, without
hand-carrying a pending-source slot between its load path and its
realize handler. On an engine that is already attached — or whose
vo never uses the render API (headless, a
windowed vo), so no attach is coming — this is plain
load.
The whole loadfile is deferred, not just an unpause, because a
load before the render context exists doesn’t merely start
blind: mpv fails to initialize the video output and drops the
video track — a video-only file dies with
MPV_ERROR_NOTHING_TO_PLAY (-16) even when loaded paused, and a
file with audio plays sound over a permanently black surface.
The queued source is a pending intent: a later
load, load_paused, or
stop before the attach supersedes it (newest
transport call wins — including the same commands issued through
command), and a second load_when_ready
replaces it. Pause
state needs no special casing — the pause property persists
across loadfile, so a consumer that pauses before the attach
gets the deferred file loaded paused, exactly as if it had been
playing.
The defer-or-load decision reads the current vo property,
so it tracks runtime vo changes (via
set_property) and values picked up from a
config file — not just what the builder set. The same rule covers
the window after a detach_render: with
vo still on the render API, sources queue again awaiting a
re-attach — a shell going render-less for good should switch vo
(e.g. to null) so loads run immediately. A deferred loadfile
that fails at attach time surfaces as
PlaybackEvent::Failed on the next
pump_events, never as an Err from the
attach call.
Sourcepub fn command(&self, name: &str, args: &[&str]) -> Result<()>
pub fn command(&self, name: &str, args: &[&str]) -> Result<()>
Escape hatch: any mpv command, args passed as an array (no quoting needed).
Commands that decide what plays next — loadfile, loadlist,
stop, quit, quit-watch-later — also discard a load queued by
load_when_ready, same as the typed
transport methods: this is the only way to issue loadfile with
flags, and a superseded source must not resurface at attach time.
Sourcepub fn set_property(
&self,
name: &str,
value: impl Into<PropertyValue>,
) -> Result<()>
pub fn set_property( &self, name: &str, value: impl Into<PropertyValue>, ) -> Result<()>
Set an mpv property. Accepts bool / i64 / f64 / &str /
String (anything Into<PropertyValue>).
Sourcepub fn get_property<T: PropertyGet>(&self, name: &str) -> Result<T>
pub fn get_property<T: PropertyGet>(&self, name: &str) -> Result<T>
Read an mpv property as bool, i64, f64, or String
(PropertyGet is sealed to those).
Sourcepub fn set_paused(&self, paused: bool) -> Result<()>
pub fn set_paused(&self, paused: bool) -> Result<()>
Pause (true) or resume (false) playback.
Sourcepub fn is_paused(&self) -> bool
pub fn is_paused(&self) -> bool
Best-effort pause-state query; false when nothing is loaded yet
— use is_idle to tell “playing” apart from
“nothing to play”.
Sourcepub fn is_idle(&self) -> bool
pub fn is_idle(&self) -> bool
True when no file is loaded (idle-active) — distinguishes
“nothing to pause” from is_paused being
false.
Sourcepub fn stop(&self) -> Result<()>
pub fn stop(&self) -> Result<()>
Stop playback and unload the current file. Surfaces as
PlaybackEvent::Ended with EndReason::Stop. Also discards a
load queued by load_when_ready — there
is nothing left to play.
Sourcepub fn set_volume(&self, percent: f64) -> Result<()>
pub fn set_volume(&self, percent: f64) -> Result<()>
Volume in percent: 0–100 is normal range, above 100 amplifies (up
to mpv’s volume-max).
Sourcepub fn position(&self) -> Option<f64>
pub fn position(&self) -> Option<f64>
Current playback position in seconds, if a file is loaded.
Sourcepub fn duration(&self) -> Option<f64>
pub fn duration(&self) -> Option<f64>
Total duration in seconds. None while mpv is still parsing or for
unknown-duration streams.
Sourcepub fn seek_absolute(&self, secs: f64) -> Result<()>
pub fn seek_absolute(&self, secs: f64) -> Result<()>
Seek to an absolute position in seconds.
Sourcepub fn seek_relative(&self, secs: f64) -> Result<()>
pub fn seek_relative(&self, secs: f64) -> Result<()>
Seek by a delta in seconds (negative seeks backward).
Sourcepub fn observe(&self, name: &str, format: PropertyFormat) -> Result<ObserveId>
pub fn observe(&self, name: &str, format: PropertyFormat) -> Result<ObserveId>
Observe a property for changes: matching
PlaybackEvent::PropertyChanged events arrive via
pump_events, starting with one carrying the
current value (handy for initializing UI state). format picks
the delivered PropertyValue variant; mpv coerces where it can.
Typical player set: pause (Flag), time-pos/duration
(Double), paused-for-cache (Flag), dwidth/dheight (Int).
Sourcepub fn set_wakeup_callback(&self, on_wakeup: impl Fn() + Send + Sync + 'static)
pub fn set_wakeup_callback(&self, on_wakeup: impl Fn() + Send + Sync + 'static)
Register a callback fired whenever mpv queues new events — the
push alternative to polling pump_events on
a timer, and the only timely signal when no frames are flowing
(audio-only playback, a load failure while paused).
The callback also fires once synchronously during this call (so the construct → share → register ordering documented on the attach methods applies here too), and mpv may additionally fire it spuriously. Treat a wakeup as “check the queue”, never “an event arrived”.
Fires on arbitrary mpv-internal threads — possibly several at
once (hence Sync), possibly re-entrantly with other engine
calls: do no work and call no engine methods inside — signal your
main loop and pump from there (the same bridging pattern as the
render-update callback). Replaces any previously registered
wakeup callback.
Sourcepub fn pump_events(&self) -> Vec<PlaybackEvent>
pub fn pump_events(&self) -> Vec<PlaybackEvent>
Drain pending mpv events into typed PlaybackEvents. Call on a
timer or after the update callback; never blocks.
Built on rsmpv’s non-blocking poll_event (&self; internally
serialized against libmpv’s one-waiter-per-handle rule). The
engine adds the pump lock on top so each drain is atomic —
concurrent pollers would otherwise split the stream, tearing
ordered sequences across callers’ batches.
Sourcepub unsafe fn attach_gl_render(
&self,
get_proc_address: ProcAddressFn,
options: GlRenderOptions,
on_update: impl Fn() + Send + Sync + 'static,
) -> Result<()>
pub unsafe fn attach_gl_render( &self, get_proc_address: ProcAddressFn, options: GlRenderOptions, on_update: impl Fn() + Send + Sync + 'static, ) -> Result<()>
Create the OpenGL render context. Call with the target GL context
current (e.g. GTK: in the GLArea realize handler).
get_proc_address resolves GL symbols (on Linux, EGL 1.5’s
eglGetProcAddress covers everything mpv asks for — beware
libepoxy on glvnd builds, which doesn’t export core GL symbols as
plain dlsym-able functions and makes mpv report
MPV_ERROR_UNSUPPORTED). on_update fires on mpv’s render
thread — and once synchronously during this call — to signal “a
new frame wants drawing”; forward it to your main loop and call
render_gl from your draw handler.
The synchronous first call arrives before the context is stored:
a render_gl from inside it no-ops (harmlessly
— mpv re-signals). It runs on the caller’s thread but outside the
engine’s render lock, so calling back into render methods cannot
deadlock.
on_update typically captures a Weak handle to your player
state — construct the Engine, wrap it in your Arc/shared
structure, then attach with the weak-capturing closure, then
load.
A successful attach also issues any load queued by
load_when_ready. Err still means “no
context was attached” — a deferred loadfile that fails here
leaves the context in place and surfaces as
PlaybackEvent::Failed on the next
pump_events (the wakeup callback fires).
options fixes the shell’s render-loop discipline at attach:
frame pacing (GlRenderOptions::block_for_target_time) and
mpv’s advanced control (GlRenderOptions::advanced_control,
which obligates render_update after every
update callback). GlRenderOptions::default is mpv’s stock
behavior.
§Safety
GL-context currency is a dynamic, per-call rule the type system
cannot capture (rsmpv’s OpenGL constructor is unsafe for the
same reason, and this crate forwards the obligation rather than
hiding it): the target GL context must be current on the calling
thread now, on every later render_gl or
render_update, and when the context is
freed — detach_render or the engine’s
drop. Violating the rule is undefined behavior.
Sourcepub fn attach_sw_render(
&self,
on_update: impl Fn() + Send + Sync + 'static,
) -> Result<()>
pub fn attach_sw_render( &self, on_update: impl Fn() + Send + Sync + 'static, ) -> Result<()>
Create the software render context: frames arrive as RGBA bytes
via render_sw, no GL anywhere — the
backend for shells that upload pixels themselves (or hand them to
a non-GL compositor). Unlike the GL backend there are no
context-current requirements, for rendering or teardown.
on_update has the same contract as in
attach_gl_render: fires on mpv’s
render thread plus once synchronously (outside the render lock,
before the context is stored), and typically captures a Weak
handle — construct, share, attach, then load.
As with attach_gl_render: a successful
attach issues any load_when_ready
queue, Err still means “no context was attached”, and a deferred
loadfile failing here surfaces as PlaybackEvent::Failed on
the next pump_events instead.
Sourcepub fn detach_render(&self)
pub fn detach_render(&self)
Drop the render context now. For the OpenGL backend, call with
the GL context still current (GTK: from the unrealize handler):
freeing without the right context current leaks mpv’s GL objects
into whatever context is current — in GTK that painted artifacts
over the whole window. The software backend has no such
requirement; detach from any thread.
After a detach, load_when_ready defers
again — vo still names the render API, so sources queue awaiting
a re-attach. A shell detaching for good (say, dropping to
audio-only) should also switch vo (e.g.
set_property("vo", "null")); the
defer-or-load decision reads the live vo, so loads then run
immediately instead of parking.
Sourcepub fn has_render(&self) -> bool
pub fn has_render(&self) -> bool
Whether a render context (of either backend) is currently
attached. Delegates to attached_render
— one read of the slot, so the two can never disagree.
Sourcepub fn attached_render(&self) -> Option<RenderKind>
pub fn attached_render(&self) -> Option<RenderKind>
Which render backend is attached, if any — the “which one”
companion to has_render, for shells that
route between per-backend code paths (say, GPU texture sampling
vs. RGBA upload) without having to track the attach outcome in
state of their own.
Sourcepub fn set_render_update_callback(
&self,
on_update: impl Fn() + Send + Sync + 'static,
) -> Result<()>
pub fn set_render_update_callback( &self, on_update: impl Fn() + Send + Sync + 'static, ) -> Result<()>
Replace the render-update callback registered at attach — the same
post-registration replaceability
set_wakeup_callback has, for the
render seam. For shells that can only build their real closure
after the engine is shared: attach with a placeholder, wrap the
engine in your Arc/shared structure, then register the
weak-capturing closure here.
The new callback takes over the attach-time contract: it fires on mpv’s render thread — and once synchronously on the calling thread, from inside this very call (registration raises an update immediately, so a frame signaled to the old callback isn’t lost). The synchronous fire runs outside every engine lock, same as at attach — an engine call from inside it cannot deadlock. The standing rule still applies to the mpv-thread fires, though: do no work and call no engine methods inside — signal your main loop and render/pump from there.
The replaced closure is released with no engine lock held: on this
thread during this call when no invocation is in flight, otherwise
when its last in-flight invocation finishes — possibly on an
mpv-internal thread, so captures whose Drop calls into libmpv
(e.g. a last Engine-owning handle) don’t belong in an update
callback.
The registration is tied to the attached context:
detach_render releases it, and the next
attach starts from that attach’s own on_update.
Errors with Error::NotAttached when no render context is
attached — a callback that could never fire is a wiring bug,
surfaced loudly rather than silently dropped.
Sourcepub fn render_update(&self) -> bool
pub fn render_update(&self) -> bool
Process pending render work after an update callback fired (never
call it from inside the callback itself — that’s forbidden, like
any other engine call there). Returns true when a new frame
should be drawn. Optional under default options; mandatory
promptly after every update callback when the GL backend was
attached with GlRenderOptions::advanced_control. false when
no backend is attached. For the GL backend, the attach contract’s
GL-currency rule covers this call too.
Sourcepub fn render_gl(&self, fbo: i32, w: i32, h: i32, flip_y: bool) -> Result<()>
pub fn render_gl(&self, fbo: i32, w: i32, h: i32, flip_y: bool) -> Result<()>
Draw the current frame into fbo (0 = default framebuffer) with
the GL context current. No-op before
attach_gl_render; errors with
Error::RenderBackendMismatch if the software backend is
attached instead. flip_y flips the output for flipped-origin
targets (GTK’s GLArea wants true). Whether this call blocks
until the frame’s target display time was fixed at attach
(GlRenderOptions::block_for_target_time; the default blocks).
Sourcepub fn render_sw(&self, w: i32, h: i32, buf: &mut Vec<u8>) -> Result<()>
pub fn render_sw(&self, w: i32, h: i32, buf: &mut Vec<u8>) -> Result<()>
Render the current frame as RGBA8 into buf (resized to
w * h * 4; alpha always opaque). Callable from any thread.
No-op before attach_sw_render —
buf is left untouched; errors with
Error::RenderBackendMismatch if the OpenGL backend is
attached instead.