pub struct Handle { /* private fields */ }
Expand description
The primary interface to mpv.
This Handle
must be created by mpv; usually it is passed in from mpv to a cplugin via mpv_open_cplugin(*mpv_handle)
.
See Handle::from_ptr()
for an example.
Implementations§
Source§impl Handle
impl Handle
Sourcepub fn from_ptr(handle: *mut mpv_handle) -> Self
pub fn from_ptr(handle: *mut mpv_handle) -> Self
Creates a Handle
from the provided pointer to a mpv_handle
.
This mpv_handle
must be created by mpv, usually passed in via mpv’s call into mpv_open_cplugin(*mpv_handle)
.
§Example
#[unsafe(no_mangle)]
extern "C" fn mpv_open_cplugin(ptr: *mut mpv_handle) -> std::os::raw::c_int {
let handle = Handle::from_ptr(ptr);
// ...
}
Sourcepub fn client_api_version() -> u64
pub fn client_api_version() -> u64
Return the MPV_CLIENT_API_VERSION
the mpv source has been compiled with.
Sourcepub fn client_name(&self) -> Result<String>
pub fn client_name(&self) -> Result<String>
Return the name of this client Handle
.
Every client has its own unique name, which is mostly used for user interface purposes.
Sourcepub fn client_id(&self) -> i64
pub fn client_id(&self) -> i64
Return the ID of this client Handle
.
Every client has its own unique ID. This ID is never reused by the core, even if the Handle
at hand gets destroyed and new handles get allocated.
Some mpv APIs (not necessarily all) accept a name in the form @id
in addition to the proper Handle::client_name()
, where id
is the ID in decimal form (e.g. @123
).
For example, the script-message-to
command takes the client name as the first argument but also accepts the client ID formatted in this manner.
Sourcepub fn create() -> Client
pub fn create() -> Client
Create a new mpv instance and an associated client API Client
to control the mpv instance.
This instance is in a pre-initialized state and needs to be initialized to be actually used with most other API functions.
Some API functions will return Error::Uninitialized
in the uninitialized state.
You can call Handle::set_property()
to set initial options.
After this, call Client::initialize()
to start the player, and then use e.g. Handle::command()
to start playback of a file.
The point of separating Client
creation and actual initialization is that you can configure things which can’t be changed during runtime.
Unlike the command line player, this will have initial settings suitable for embedding in applications. The following settings are different:
stdin
/stdout
/stderr
and the terminal will never be accessed. This is equivalent to setting the--terminal=no
option. (Technically, this also suppresses C signal handling.)- No config files will be loaded. This is roughly equivalent to using
--no-config
. Since libmpv 1.15, you can actually re-enable this option, which will make libmpv load config files duringClient::initialize()
. If you do this, you are strongly encouraged to set theconfig-dir
option too. (Otherwise it will load the mpv command line player’s config.) - Idle mode is enabled, which means the playback core will enter idle mode if there are no more files to play on the internal playlist, instead of exiting.
This is equivalent to the
--idle
option. - Disable parts of input handling.
- Most of the different settings can be viewed with the command line player by running
mpv --show-profile=libmpv
.
All this assumes that API users want an mpv instance that is strictly isolated from the command line player’s configuration, user settings, and so on. You can re-enable disabled features by setting the appropriate options.
The mpv command line parser is not available through this API, but you can set individual options with Handle::set_property()
.
Files for playback must be loaded with Handle::command()
or others.
§Concurrency
Note that you should avoid doing concurrent accesses on the uninitialized client handle. (Whether concurrent access is definitely allowed or not has yet to be decided by mpv.)
Sourcepub fn create_client(&self, name: &str) -> Result<Client>
pub fn create_client(&self, name: &str) -> Result<Client>
Create a new Client
connected to the same player core as self
.
This context has its own event queue, its own Handle::request_event()
state, its own Handle::request_log_messages()
state,
its own set of observed properties, and its own state for asynchronous operations. Otherwise, everything is shared.
This client should be destroyed with Client::destroy()
if no longer needed.
The core will live as long as there is at least 1 handle referencing it.
Any handle can make the core quit, which will result in every handle receiving Event::Shutdown
.
Sourcepub fn create_weak_client(&self, name: &str) -> Result<Client>
pub fn create_weak_client(&self, name: &str) -> Result<Client>
This is the same as Handle::create_client()
, but the created mpv_handle
is treated as a weak reference.
If all handles referencing a core are weak references, the core is automatically destroyed. (This still goes through normal shutdown, of course.
Effectively, if the last non-weak handle is destroyed, then the weak handles receive Event::Shutdown
and are asked to terminate as well.)
Note if you want to use this like refcounting: you have to be aware that Client::terminate_destroy()
and Client::destroy()
for the last non-weak mpv_handle
will block until all weak handles are destroyed.
Sourcepub fn load_config_file(&self, filename: &str) -> Result<()>
pub fn load_config_file(&self, filename: &str) -> Result<()>
Load a config file. This parses the file and sets every entry in the config file’s default section as if Handle::set_option()
is called.
The filename should be an absolute path. If it isn’t, the actual path used is unspecified. (Note: an absolute path starts with ‘/
’ on UNIX.)
If the file wasn’t found, Error::InvalidParameter
is returned.
If a fatal error happens when parsing a config file, Error::OptionError
is returned.
Errors when setting options as well as other types or errors are ignored (even if options do not exist).
You can still try to capture the resulting error messages with Handle::request_log_messages()
.
Note that it’s possible that some options were successfully set even if any of these errors happen.
Sourcepub fn get_time_ns(&self) -> i64
pub fn get_time_ns(&self) -> i64
Return the internal time in nanoseconds. This has an arbitrary start offset but will never wrap or go backwards.
Note that this is always the real time and doesn’t necessarily have to do with playback time.
For example, playback could go faster or slower due to playback speed, or due to playback being paused.
Use the time-pos
property instead to get the playback status.
Unlike other libmpv APIs, this can be called at absolutely any time (even within wakeup callbacks), as long as the Handle
is valid.
Safe to be called from mpv render API threads.
Sourcepub fn get_time_us(&self) -> i64
pub fn get_time_us(&self) -> i64
Same as Handle::get_time_ns
but in microseconds.
Sourcepub fn set_option<T: MpvSend>(&self, name: &str, data: T) -> Result<()>
👎Deprecated: For most purposes, this is not needed anymore.Starting with mpv version 0.21.0 (version 1.23) most options can be set with Handle::set_property()
(and related functions), and even before Handle::initialize()
.In some obscure corner cases, using this function to set options might still be required (see “Inconsistencies between options and properties” in the manpage).Once these are resolved, the option setting functions might be fully deprecated.
pub fn set_option<T: MpvSend>(&self, name: &str, data: T) -> Result<()>
Handle::set_property()
(and related functions), and even before Handle::initialize()
.In some obscure corner cases, using this function to set options might still be required (see “Inconsistencies between options and properties” in the manpage).Once these are resolved, the option setting functions might be fully deprecated.Set an option. Note that you can’t normally set options during runtime. It works in an uninitialized state (see Handle::create()
), and in some cases in at runtime.
Using a format other than Node
is equivalent to constructing a Node
with the given format and data and passing it to this function.
§Example
handle.set_option("idle", "yes")?;
Sourcepub fn command(&self, command: &[impl AsRef<str>]) -> Result<()>
pub fn command(&self, command: &[impl AsRef<str>]) -> Result<()>
Send a command to the player. Commands are the same as those used in input.conf
, except that this function takes parameters in a pre-split form.
The commands and their parameters are documented in input.rst.
Does not use OSD and string expansion by default (unlike Handle::command_string()
and input.conf).
§Params
command
- Usually, the first item is the command, and the following items are arguments.
§Example
handle.command(&["script-message-to", "commands", "type", "seek absolute-percent", "6"])?;
Sourcepub fn command_node(&self, command: Node) -> Result<Node>
pub fn command_node(&self, command: Node) -> Result<Node>
Same as Handle::command_ret()
, but allows passing structured data in any format.
In particular, calling Handle::command()
is exactly like calling Handle::command_node()
with the format set to NodeArray
,
and every arg passed in order as String
.
Does not use OSD and string expansion by default.
§Params
The command
Node
can be one of the following formats:
Node::Array
: Positional arguments. Each entry is an argument using an arbitrary format (the format must be compatible with the used command). Usually, the first item is the command name (as aNode::String
). The order of arguments is as documented in each command description.Node::Map
: Named arguments. This requires at least an entry with the key “name” to be present, which must be a string and contain the command name. The special entry “_flags” is optional, and if present, must be an array of strings, each being a command prefix to apply. All other entries are interpreted as arguments. They must use the argument names as documented in each command description. Some commands do not support named arguments at all and must useNode::Array
.
§Return
If the function succeeds, Result<Node>
is command-specific return data. Few commands actually use this.
§Example
// For convenience, you use node_array!(), which accepts any arbitrary types
// implementing Into<Node> and produces a Node::Array...
handle.command_node(node_array!("frame-step", 20, "mute"))?;
// ...or node_map!(), which is similar but takes (Into<String>, Into<Node>) tuples
// and produces a Node::Map.
handle.command_node(node_map! {
("name", "show-text"),
("text", "peekaboo!"),
("duration", 500),
})?;
Sourcepub fn command_ret(&self, command: &[impl AsRef<str>]) -> Result<Node>
pub fn command_ret(&self, command: &[impl AsRef<str>]) -> Result<Node>
This is essentially identical to Handle::command()
, but it also returns a result.
Does not use OSD and string expansion by default.
§Params
command
- Usually, the first item is the command, and the following items are arguments.
§Return
If the function succeeds, Result<Node>
is command-specific return data. Few commands actually use this.
Sourcepub fn command_string(&self, command: &str) -> Result<()>
pub fn command_string(&self, command: &str) -> Result<()>
Same as Handle::command()
, but use input.conf parsing for splitting arguments.
This is slightly simpler, but also more error-prone, since arguments may need quoting/escaping.
This also has OSD and string expansion enabled by default.
Sourcepub fn set_property<T: MpvSend>(&self, name: &str, value: T) -> Result<()>
pub fn set_property<T: MpvSend>(&self, name: &str, value: T) -> Result<()>
Set a property to a given value.
Properties are essentially variables that can be queried or set at runtime. For example, writing to the pause property will actually pause or unpause playback.
§Params
If the MpvFormat::MPV_FORMAT
of value
doesn’t match with the internal mpv_format
format of the property,
access usually will fail with Error::PropertyFormat
.
In some cases, the data is automatically converted and access succeeds. For example, mpv converts i64
to f64
,
and access using String
usually invokes a string parser.
The same happens when calling this function with Node
: the underlying format may be converted to another type if possible.
Using a format other than Node
is equivalent to constructing a Node
with the given format and data and passing it to this function.
§Example
handle.set_property("chapter", 3)?;
Sourcepub fn del_property(&self, name: &str) -> Result<()>
pub fn del_property(&self, name: &str) -> Result<()>
Convenience function to delete a property.
This is equivalent to running the command del [name]
.
Sourcepub fn get_property<T: MpvRecv>(&self, name: &str) -> Result<T>
pub fn get_property<T: MpvRecv>(&self, name: &str) -> Result<T>
Read the value of the given property.
If the MpvFormat::MPV_FORMAT
of the requested type doesn’t match with the internal mpv_format
format of the property,
access usually will fail with Error::PropertyFormat
.
In some cases, the data is automatically converted and access succeeds. For example, i64
is always converted to f64
,
and access using String
usually invokes a string formatter.
§Example
// use turbofish...
let duration = handle.get_property::<f64>("duration")?;
// or explicitly type the assignment...
let node: Node = handle.get_property("metadata")?;
Sourcepub fn observe_property(
&self,
name: &str,
format: Format,
userdata: u64,
) -> Result<()>
pub fn observe_property( &self, name: &str, format: Format, userdata: u64, ) -> Result<()>
Get a notification whenever the given property changes.
You will receive updates as Event::PropertyChange
. Note that this is not very precise: for some properties, it may not send updates even if the property changed.
This depends on the property, and it’s a valid feature request to ask for better update handling of a specific property.
(For some properties, like clock
, which shows the wall clock, this mechanism doesn’t make too much sense anyway.)
Property changes are coalesced: the change events are returned only once the event queue becomes empty
(e.g., Handle::wait_event()
would block or return Event::None
), and then only one event per changed property is returned.
You always get an initial change notification. This is meant to initialize the user’s state to the current value of the property.
Normally, change events are sent only if the property value changes within the requested format.
PropertyChange.value
will contain the PropertyValue
.
If the property is observed with the format parameter set to PropertyValue::None
, you get low-level notifications whether the property may have changed.
With this mode, you will have to determine yourself whether the property really changed. On the other hand, this mechanism can be faster and use fewer resources.
Observing a property that doesn’t exist is allowed. (Although it may still cause some sporadic change events.)
Keep in mind that you will get Event::PropertyChange
even if you change a property yourself.
Try to avoid endless feedback loops, which could happen if you react to the change notifications triggered by your own change.
Only the Handle
on which this was called will receive Event::PropertyChange
events or can unobserve them.
§Warning
If a property is unavailable or retrieving it caused an error, Event::PropertyChange
’s PropertyChange.value
will be PropertyValue::None
,
even if the format parameter was set to a different value.
§Params
-
userdata
: This will be used for thePropertyChange.userdata
field for the receivedEvent::PropertyChange
events.If you have no use for this, pass 0.
Also see Handle::unobserve_property()
.
§Example
// you can set userdata = 0 if you don't plan un unobserving the value later
handle.observe_property("playtime-remaining", Format::DOUBLE, 0)?;
Sourcepub fn unobserve_property(&self, userdata: u64) -> Result<i32>
pub fn unobserve_property(&self, userdata: u64) -> Result<i32>
Undo Handle::observe_property
.
This will remove all observed properties for which the given number was passed as userdata
to Handle::observe_property()
.
§Params
userdata
:userdata
that was passed toHandle::observe_property()
§Returns
Result<i32>
contains the number of properties removed on success.
§Example
// if you want to later unobserve a property, you must provide a userdata
let media_title_userdata: u64 = 12345; // arbitrary, user-defined value
handle.observe_property("media-title", Format::STRING, media_title_userdata)?;
// later...
handle.unobserve_property(media_title_userdata)?;
Sourcepub fn request_event(&self, event_id: EventId, enable: bool) -> Result<()>
pub fn request_event(&self, event_id: EventId, enable: bool) -> Result<()>
Enable or disable an Event
given its EventId
.
Some events are enabled by default. Some events can’t be disabled.
(Informational note: currently, all events are enabled by default, except Event::Tick
.)
Sourcepub fn request_log_messages(&self, max_level: LogLevel) -> Result<()>
pub fn request_log_messages(&self, max_level: LogLevel) -> Result<()>
Enable or disable receiving of log messages.
These are the messages the command line player prints to the terminal.
This call sets the maximum log level for a message to be received with Event::LogMessage
.
§Params
max_level
: Maximum log level to subscribe to.
The value LogLevel::None
disables all messages. This is the default.
Sourcepub fn wait_event(&self, timeout: f64) -> Result<Event>
pub fn wait_event(&self, timeout: f64) -> Result<Event>
Wait for the next event, or until the timeout expires, or if another thread makes a call to Handle::wakeup()
.
See Event
for the possible events.
§Params
timeout
: Timeout in seconds, after which the function returns even if no event was received. AnEvent::None
is returned on timeout.- A value of 0 will disable waiting and is suitable for polling.
- Negative values will wait with an infinite timeout.
§Warning
The internal event queue has a limited size (per client handle). If you don’t empty the event queue quickly enough with Handle::wait_event()
,
it will overflow and silently discard further events. If this happens, making asynchronous requests will fail as well (with Error::EventQueueFull
).
§Concurrency
Only one thread is allowed to call this on the same Handle
at a time. The API won’t complain if more than one thread calls this,
but it will cause race conditions in the client when accessing the shared mpv_event
struct.
Note that most other API functions are not restricted by this, and no API function internally calls Handle::wait_event()
.
Additionally, concurrent calls to different Handle
s are always safe.
§Example
match handle.wait_event(0.0)? {
Event::None => println!("No event was ready yet!"),
Event::Shutdown => {
println!("Shutting down!");
// You must cleanly exit after receiving Event::Shutdown, or else you'll hang mpv.
return Ok(());
}
Event::LogMessage(log_message) => println!("Got a log message: {log_message:?}"),
event => println!("Got an other event: {event:?}"),
}
§Warning
cplugins must call Handle::wait_event()
at least once after initialization;
mpv will block awaiting a sign of life:.
#[unsafe(no_mangle)]
extern "C" fn mpv_open_cplugin(ptr: *mut mpv_handle) -> std::os::raw::c_int {
let handle = Handle::from_ptr(ptr);
println!("Sleeping 5 seconds pre-wait_event...");
// mpv will be completely hung during this sleep...
sleep(Duration::from_secs(5));
// Let mpv know we're alive!
let _ = handle.wait_event(-1.0);
println!("Sleeping 15 seconds post-wait_event...");
// mpv will operate normally during this sleep.
sleep(Duration::from_secs(15));
return 0;
}
Sourcepub fn wakeup(&self)
pub fn wakeup(&self)
Interrupt the current Handle::wait_event()
call.
This will wake up the thread currently waiting in Handle::wait_event()
. If no thread is waiting, the next Handle::wait_event()
call will return immediately (this is to avoid lost wakeups).
Handle::wait_event()
will receive an Event::None
if it’s woken up due to this call. But note that this dummy event might be
skipped if there are already other events queued. All that counts is that the waiting thread is woken up.
Sourcepub fn hook_add(&self, userdata: u64, name: &str, priority: i32) -> Result<()>
pub fn hook_add(&self, userdata: u64, name: &str, priority: i32) -> Result<()>
A hook is like a synchronous event that blocks the player. You register a hook handler with this function. You will get an event,
which you need to handle, and once things are ready, you can let the player continue with Handle::hook_continue()
.
Currently, hooks can’t be removed explicitly. But they will be implicitly removed if the Handle
it was registered with is destroyed.
This also continues the hook if it was being handled by the destroyed handle (but this should be avoided, as it might mess up the order of hook execution).
See the “Hooks” section in the manpage to see which hooks are currently defined.
Some hooks might be reentrant (so you get multiple Event::Hook
for the same hook). If this can happen for a specific hook type,
it will be explicitly documented in the manpage.
Only the Handle
on which this was called will receive the hook events or can “continue” them.
§Priority
Hook handlers are ordered globally by priority and order of registration. Handlers for the same hook with the same priority are invoked in order of registration (the handler registered first is run first). Handlers with lower priority are run first (which seems backward).
§Params
userdata
: This will be used for theEvent::Hook.userdata
field for the receivedEvent::Hook
events. If you have no use for this, pass 0.name
: The hook name. This should be one of the documented names. But if the name is unknown, the hook event will simply never be raised.priority
: See remarks above. Use 0 as a neutral default.
Sourcepub fn hook_continue(&self, id: u64) -> Result<()>
pub fn hook_continue(&self, id: u64) -> Result<()>
Respond to an Event::Hook
event. You must call this after you have handled the event.
There is no way to “cancel” or “stop” the hook.
Calling this will typically unblock the player for whatever the hook is responsible for (e.g., for the on_load
hook it lets it continue playback).
§Params
id
: This must be the value of theHook.id
field for the correspondingEvent::Hook
.
§Warning
It is explicitly undefined behavior to call this more than once for each Event::Hook
, to pass an incorrect ID,
or to call this on a Handle
different from the one that registered the handler and received the event.
§Example
match handle.wait_event(0.0)? {
Event::Hook(hook) => {
do_something_during_hook();
// You MUST call hook_continue() on the provided Hook.id,
// or else you'll hang mpv.
handle.hook_continue(hook.id)?;
}
// ...
event => {}
}