AnyEvent-MPV
view release on metacpan or search on metacpan
the "loadfile" command itself will run successfully.
To unpause, we send another command, "set", to set the "pause" property
to "no", this time using the "cmd" method, which queues the command, but
instead of waiting for a reply, it immediately returns a condvar that
cna be used to receive results.
This should then cause mpv to start playing the video.
It then again waits two seconds and quits.
Now, just waiting two seconds is rather, eh, unuseful, so let's look at
receiving events (using a somewhat embellished example):
use AnyEvent;
use AnyEvent::MPV;
my $videofile = "xyzzy.mkv";
my $quit = AE::cv;
my $mpv = AnyEvent::MPV->new (
trace => 1,
args => ["--pause", "--idle=yes"],
);
$mpv->start;
$mpv->register_event (start_file => sub {
$mpv->cmd ("set", "pause", "no");
});
$mpv->register_event (end_file => sub {
my ($mpv, $event, $data) = @_;
print "end-file<$data->{reason}>\n";
$quit->send;
});
$mpv->cmd (loadfile => $mpv->escape_binary ($videofile));
$quit->recv;
This example uses a global condvar $quit to wait for the file to finish
playing. Also, most of the logic is now implement in event handlers.
The two events handlers we register are "start-file", which is emitted
by mpv once it has loaded a new file, and "end-file", which signals the
end of a file (underscores are internally replaced by minus signs, so
you cna speicfy event names with either).
In the "start-file" event, we again set the "pause" property to "no" so
the movie starts playing. For the "end-file" event, we tell the main
program to quit by invoking $quit.
This should conclude the basics of operation. There are a few more
examples later in the documentation.
ENCODING CONVENTIONS
As a rule of thumb, all data you pass to this module to be sent to mpv
is expected to be in unicode. To pass something that isn't, you need to
escape it using "escape_binary".
Data received from mpv, however, is *not* decoded to unicode, as data
returned by mpv is not generally encoded in unicode, and the encoding is
usually unspecified. So if you receive data and expect it to be in
unicode, you need to first decode it from UTF-8, but note that this
might fail. This is not a limitation of this module - mpv simply does
not specify nor guarantee a specific encoding, or any encoding at all,
in its protocol.
METHODS
$mpv = AnyEvent::MPV->new (key => value...)
Creates a new "mpv" object, but does not yet do anything. The
support key-value pairs are:
mpv => $path
The path to the mpv binary to use - by default, "mpv" is used
and therefore, uses your "PATH" to find it.
args => [...]
Arguments to pass to mpv. These arguments are passed after the
hardcoded arguments used by this module, but before the
arguments passed ot "start". It does not matter whether you
specify your arguments using this key, or in the "start" call,
but when you invoke mpv multiple times, typically the arguments
used for all invocations go here, while arguments used for
specific invocations (e..g filenames) are passed to "start".
trace => false|true|coderef
Enables tracing if true. In trace mode, output from mpv is
printed to standard error using a "mpv>" prefix, and commands
sent to mpv are printed with a ">mpv" prefix.
If a code reference is passed, then instead of printing to
standard errort, this coderef is invoked with a first arfgument
being either "mpv>" or ">mpv", and the second argument being a
string to display. The default implementation simply does this:
sub {
warn "$_[0] $_[1]\n";
}
on_eof => $coderef->($mpv)
on_event => $coderef->($mpv, $event, $data)
on_key => $coderef->($mpv, $string)
These are invoked by the default method implementation of the
same name - see below.
$string = $mpv->escape_binary ($string)
This module excects all command data sent to mpv to be in unicode.
Some things are not, such as filenames. To pass binary data such as
filenames through a comamnd, you need to escape it using this
method.
The simplest example is a "loadfile" command:
$mpv->cmd_recv (loadfile => $mpv->escape_binary ($path));
$started = $mpv->start (argument...)
Starts mpv, passing the given arguemnts as extra arguments to mpv.
If mpv is already running, it returns false, otherwise it returns a
true value, so you can easily start mpv on demand by calling "start"
just before using it, and if it is already running, it will not be
started again.
The arguments passwd to mpv are a set of hardcoded built-in
arguments, followed by the arguments specified in the constructor,
followed by the arguments passwd to this method. The built-in
arguments currently are --no-input-terminal, --really-quiet (or
--quiet in "trace" mode), and "--input-ipc-client" (or equivalent).
Some commonly used and/or even useful arguments you might want to
pass are:
--idle=yes or --idle=once to keep mpv from quitting when you don't
specify a file to play.
--pause, to keep mpv from instantly starting to play a file, in case
you want to inspect/change properties first.
--force-window=no (or similar), to keep mpv from instantly opening a
window, or to force it to do so.
--audio-client-name=yourappname, to make sure audio streams are
associated witht eh right program.
--wid=id, to embed mpv into another application.
--no-terminal, --no-input-default-bindings, --no-input-cursor,
--input-conf=/dev/null, --input-vo-keyboard=no - to ensure only you
control input.
The return value can be used to decide whether mpv needs
initializing:
if ($mpv->start) {
$mpv->bind_key (...);
$mpv->cmd (set => property => value);
...
}
You can immediately starting sending commands when this method
returns, even if mpv has not yet started.
$mpv->stop
Ensures that mpv is being stopped, by killing mpv with a "TERM"
signal if needed. After this, you can "->start" a new instance
again.
$mpv->on_eof
This method is called when mpv quits - usually unexpectedly. The
default implementation will call the "on_eof" code reference
specified in the constructor, or do nothing if none was given.
For subclassing, see *SUBCLASSING*, below.
name of the property and the new value.
For a list of properties that you can observe, see the mpv
documentation <https://mpv.io/manual/stable/#property-list>.
Due to the (sane :) way mpv handles these requests, you will always
get a property cxhange event right after registering an observer
(meaning you don't have to query the current value), and it is also
possible to register multiple observers for the same property - they
will all be handled properly.
When called in void context, the observer stays in place until mpv
is stopped. In any otrher context, these methods return a guard
object that, when it goes out of scope, unregisters the observe
using "unobserve_property".
Internally, this method uses observer ids of 2**52
(0x10000000000000) or higher - it will not interfere with lower
ovserver ids, so it is possible to completely ignore this system and
execute "observe_property" commands yourself, whilst listening to
"property-change" events - as long as your ids stay below 2**52.
Example: register observers for changtes in "aid" and "sid". Note
that a dummy statement is added to make sure the method is called in
void context.
sub register_observers {
my ($mpv) = @_;
$mpv->observe_property (aid => sub {
my ($mpv, $name, $value) = @_;
print "property aid (=$name) has changed to $value\n";
});
$mpv->observe_property (sid => sub {
my ($mpv, $name, $value) = @_;
print "property sid (=$name) has changed to $value\n";
});
() # ensure the above method is called in void context
}
SUBCLASSING
Like most perl objects, "AnyEvent::MPV" objects are implemented as
hashes, with the constructor simply storing all passed key-value pairs
in the object. If you want to subclass to provide your own "on_*"
methods, be my guest and rummage around in the internals as much as you
wish - the only guarantee that this module dcoes is that it will not use
keys with double colons in the name, so youc an use those, or chose to
simply not care and deal with the breakage.
If you don't want to go to the effort of subclassing this module, you
can also specify all event handlers as constructor keys.
EXAMPLES
Here are some real-world code snippets, thrown in here mainly to give
you some example code to copy.
doomfrontend
At one point I replaced mythtv-frontend by my own terminal-based video
player (based on rxvt-unicode). I toyed with the diea of using mpv's
subtitle engine to create the user interface, but that is hard to use
since you don't know how big your letters are. It is also where most of
this modules code has originally been developed in.
It uses a unified input queue to handle various remote controls, so its
event handling needs are very simple - it simply feeds all events into
the input queue:
my $mpv = AnyEvent::MPV->new (
mpv => $MPV,
args => \@MPV_ARGS,
on_event => sub {
input_feed "mpv/$_[1]", $_[2];
},
on_key => sub {
input_feed $_[1];
},
on_eof => sub {
input_feed "mpv/quit";
},
);
...
$mpv->start ("--idle=yes", "--pause", "--force-window=no");
It also doesn't use complicated command line arguments - the file search
options have the most impact, as they prevent mpv from scanning
directories with tens of thousands of files for subtitles and more:
--audio-client-name=doomfrontend
--osd-on-seek=msg-bar --osd-bar-align-y=-0.85 --osd-bar-w=95
--sub-auto=exact --audio-file-auto=exact
Since it runs on a TV without a desktop environemnt, it tries to keep
complications such as dbus away and the screensaver happy:
# prevent xscreensaver from doing something stupid, such as starting dbus
$ENV{DBUS_SESSION_BUS_ADDRESS} = "/"; # prevent dbus autostart for sure
$ENV{XDG_CURRENT_DESKTOP} = "generic";
It does bind a number of keys to internal (to doomfrontend) commands:
for (
List::Util::pairs qw(
ESC return
q return
ENTER enter
SPACE pause
[ steprev
] stepfwd
j subtitle
BS red
i green
o yellow
b blue
D triangle
UP up
DOWN down
RIGHT right
( run in 0.796 second using v1.01-cache-2.11-cpan-39bf76dae61 )