mpv_engine/lib.rs
1//! # mpv-engine
2//!
3//! Toolkit-agnostic core for embedding libmpv: handle lifecycle, commands,
4//! properties, typed playback events, and render seams. Extracted from two
5//! independent embedders — a GTK4 app (GLArea direct-to-FBO) and an iced
6//! widget (side-context + readback/GPU export) — after their engine layers
7//! converged on the same shape.
8//!
9//! What belongs here is decided by one rule: **a quirk lives in this crate
10//! if its consumer-facing interface survives the upstream fix**; anything
11//! whose shape would change with a toolkit's API belongs in that toolkit's
12//! adapter crate. See README for the seam map and roadmap.
13//!
14//! The playback surface covers what a player UI consumes: transport
15//! (load — immediate, paused, or deferred until a render context
16//! attaches ([`Engine::load_when_ready`]) — pause/seek/stop), the mixer
17//! set (volume/mute/speed), typed
18//! lifecycle events ([`PlaybackEvent`] — including seek-completion for
19//! scrubber snap and end reasons for playlist logic), and push-based
20//! property observation ([`Engine::observe`]) for state that polling
21//! can't track well (duration becoming known, external pause flips, buffering,
22//! video dimensions). Everything else goes through the property/command
23//! escape hatches — which speak crate-owned types ([`PropertyValue`],
24//! the sealed [`PropertyGet`]), so the underlying binding's traits never
25//! enter this crate's public API.
26//!
27//! The render backends share one attach slot: OpenGL
28//! ([`Engine::attach_gl_render`] / [`Engine::render_gl`]) and software
29//! ([`Engine::attach_sw_render`] / [`Engine::render_sw`], RGBA into a
30//! caller buffer, no GL anywhere). [`Engine::attached_render`] reports
31//! which one is live ([`RenderKind`]), and the render-update callback
32//! fixed at attach can be replaced afterwards
33//! ([`Engine::set_render_update_callback`]) for shells whose real
34//! closure only exists once the engine is shared. GL attach takes
35//! [`GlRenderOptions`]
36//! to fix the shell's render-loop discipline: whether `render_gl` blocks
37//! until the frame's target time (right for a toolkit paint handler,
38//! wrong on a compositor thread), and mpv's advanced control (which
39//! obligates [`Engine::render_update`] after every update callback).
40//! On macOS, Linux and Windows the non-default `export` feature adds a
41//! third, fully safe backend (`Engine::attach_exported_render`): the
42//! engine renders on a hidden GL context of its own and hands the shell
43//! zero-copy exportable frames (`ExportedFrame` — IOSurface-backed on
44//! macOS, DMA-BUF-backed on Linux, shared-D3D11-texture-backed on
45//! Windows) for Metal / Vulkan / D3D / wgpu import — no GL and no pixel
46//! copies in the shell. The `wgpu` feature (implies
47//! `export`, wgpu-major-locked) adds
48//! `ExportedFrame::into_wgpu_texture`,
49//! which wraps the frame as a `wgpu::Texture` on the shell's device
50//! (Metal on macOS, Vulkan on Linux, DX12 on Windows) and leaves the
51//! frame's memory in wgpu's own GPU-completion tracking — no manual
52//! hold-until-done.
53//! Event delivery is pull-based
54//! ([`Engine::pump_events`]) with an optional push signal
55//! ([`Engine::set_wakeup_callback`]) for shells that don't want a
56//! polling timer.
57//!
58//! Quirks this crate owns so consumers don't have to rediscover them:
59//! - `LC_NUMERIC=C` forced before `mpv_create` (toolkits setlocale behind
60//! your back; mpv's number parsing breaks under comma-decimal locales).
61//! - Render context freed strictly before the mpv handle — structural
62//! since rsmpv 0.2 (the context co-owns the core) — and, for OpenGL,
63//! only with the GL context current: an obligation
64//! [`Engine::attach_gl_render`] carries as its `unsafe` contract (see
65//! [`Engine::detach_render`]).
66//! - Commands pass pre-tokenized argument arrays (`mpv_command`), so paths
67//! with spaces/quotes need no escaping — pinned by a regression test.
68//! - Errored end-of-file surfaces as a typed
69//! [`PlaybackEvent::Failed`] carrying the raw `mpv_error` code (for
70//! integrators mapping to their own error copy) and a diagnostic
71//! message (mpv's `mpv_error_string` text plus the numeric code).
72//! - The update callback's threading contract (mpv render thread, plus one
73//! synchronous call at registration) is documented at the seam.
74//!
75//! No toolkit dependencies, no git dependencies, no main-loop opinions:
76//! events are *pumped*, and the update callback is `Send + Sync` —
77//! bridging to a main loop is the shell's job, because that part is
78//! toolkit-shaped.
79
80#![warn(missing_docs)]
81
82mod engine;
83mod error;
84#[cfg(export_backend)]
85mod export;
86mod render;
87
88pub use engine::{
89 EndReason, Engine, EngineBuilder, ObserveId, PlaybackEvent, PropertyFormat, PropertyGet,
90 PropertyValue,
91};
92pub use error::{Error, Result};
93#[cfg(export_backend)]
94pub use export::{ExportOptions, ExportedFrame};
95#[cfg(wgpu_backend)]
96pub use export::{REQUIRED_WGPU_FEATURES, WGPU_BACKEND};
97pub use render::{GlRenderOptions, ProcAddressFn, RenderKind};