1 |
root |
1.1 |
=head1 NAME |
2 |
|
|
|
3 |
|
|
AnyEvent::MPV - remote control mpv (https://mpv.io) |
4 |
|
|
|
5 |
|
|
=head1 SYNOPSIS |
6 |
|
|
|
7 |
|
|
use AnyEvent::MPV; |
8 |
|
|
|
9 |
|
|
=head1 DESCRIPTION |
10 |
|
|
|
11 |
root |
1.5 |
This module allows you to remote control F<mpv> (a video player). It also |
12 |
|
|
is an L<AnyEvent> user, you need to make sure that you use and run a |
13 |
|
|
supported event loop. |
14 |
|
|
|
15 |
|
|
There are other modules doing this, and I haven't looked much at them |
16 |
|
|
other than to decide that they don't handle encodings correctly, and since |
17 |
|
|
none of them use AnyEvent, I wrote my own. When in doubt, have a look at |
18 |
|
|
them, too. |
19 |
|
|
|
20 |
|
|
Knowledge of the L<mpv command |
21 |
|
|
interface|https://mpv.io/manual/stable/#command-interface> is required to |
22 |
|
|
use this module. |
23 |
|
|
|
24 |
|
|
Features of this module are: |
25 |
|
|
|
26 |
|
|
=over |
27 |
|
|
|
28 |
|
|
=item uses AnyEvent, so integrates well into most event-based programs |
29 |
|
|
|
30 |
|
|
=item supports asynchronous and synchronous operation |
31 |
|
|
|
32 |
|
|
=item allows you to properly pass binary filenames |
33 |
|
|
|
34 |
|
|
=item accepts data encoded in any way (does not crash when mpv replies with non UTF-8 data) |
35 |
|
|
|
36 |
|
|
=item features a simple keybind/event system |
37 |
|
|
|
38 |
|
|
=back |
39 |
|
|
|
40 |
|
|
=head2 OVERVIEW OF OPERATION |
41 |
|
|
|
42 |
|
|
This module forks an F<mpv> process and uses F<--input-ipc-client> (or |
43 |
|
|
equivalent) to create a bidirectional communication channel between it and |
44 |
|
|
the F<mpv> process. |
45 |
|
|
|
46 |
|
|
It then speaks the somewhat JSON-looking (but not really being JSON) |
47 |
|
|
protocol that F<mpv> implements to both send it commands, decode and |
48 |
|
|
handle replies, and handle asynchronous events. |
49 |
|
|
|
50 |
|
|
Here is a very simple client: |
51 |
|
|
|
52 |
|
|
use AnyEvent; |
53 |
|
|
use AnyEvent::MPV; |
54 |
|
|
|
55 |
|
|
my $videofile = "./xyzzy.mp4"; |
56 |
|
|
|
57 |
|
|
my $mpv = AnyEvent::MPV->new (trace => 1); |
58 |
|
|
|
59 |
|
|
$mpv->start ("--", $videofile); |
60 |
|
|
|
61 |
|
|
my $timer = AE::timer 2, 0, my $quit = AE::cv; |
62 |
|
|
$quit->recv; |
63 |
|
|
|
64 |
|
|
This starts F<mpv> with the two arguments C<--> and C<$videofile>, which |
65 |
|
|
it should load and play. It then waits two seconds by starting a timer and |
66 |
|
|
quits. The C<trace> argument to the constructor makes F<mpv> more verbose |
67 |
root |
1.6 |
and also prints the commands and responses, so you can have an idea what |
68 |
root |
1.5 |
is going on. |
69 |
|
|
|
70 |
root |
1.6 |
In my case, the above example would output something like this: |
71 |
|
|
|
72 |
|
|
[uosc] Disabled because original osc is enabled! |
73 |
|
|
mpv> {"event":"start-file","playlist_entry_id":1} |
74 |
|
|
mpv> {"event":"tracks-changed"} |
75 |
|
|
(+) Video --vid=1 (*) (h264 480x480 30.000fps) |
76 |
|
|
mpv> {"event":"metadata-update"} |
77 |
|
|
mpv> {"event":"file-loaded"} |
78 |
|
|
Using hardware decoding (nvdec). |
79 |
|
|
mpv> {"event":"video-reconfig"} |
80 |
|
|
VO: [gpu] 480x480 cuda[nv12] |
81 |
|
|
mpv> {"event":"video-reconfig"} |
82 |
|
|
mpv> {"event":"playback-restart"} |
83 |
|
|
|
84 |
root |
1.5 |
This is not usually very useful (you could just run F<mpv> as a simple |
85 |
|
|
shell command), so let us load the file at runtime: |
86 |
|
|
|
87 |
|
|
use AnyEvent; |
88 |
|
|
use AnyEvent::MPV; |
89 |
|
|
|
90 |
|
|
my $videofile = "./xyzzy.mp4"; |
91 |
|
|
|
92 |
|
|
my $mpv = AnyEvent::MPV->new ( |
93 |
|
|
trace => 1, |
94 |
|
|
args => ["--pause", "--idle=yes"], |
95 |
|
|
); |
96 |
|
|
|
97 |
|
|
$mpv->start; |
98 |
|
|
$mpv->cmd_recv (loadfile => $mpv->escape_binary ($videofile)); |
99 |
|
|
$mpv->cmd ("set", "pause", "no"); |
100 |
|
|
|
101 |
|
|
my $timer = AE::timer 2, 0, my $quit = AE::cv; |
102 |
|
|
$quit->recv; |
103 |
|
|
|
104 |
|
|
This specifies extra arguments in the constructor - these arguments are |
105 |
|
|
used every time you C<< ->start >> F<mpv>, while the arguments to C<< |
106 |
|
|
->start >> are only used for this specific clal to0 C<start>. The argument |
107 |
|
|
F<--pause> keeps F<mpv> in pause mode (i.e. it does not play the file |
108 |
|
|
after loading it), and C<--idle=yes> tells F<mpv> to not quit when it does |
109 |
|
|
not have a playlist - as no files are specified on the command line. |
110 |
|
|
|
111 |
|
|
To load a file, we then send it a C<loadfile> command, which accepts, as |
112 |
|
|
first argument, the URL or path to a video file. To make sure F<mpv> does |
113 |
|
|
not misinterpret the path as a URL, it was prefixed with F<./> (similarly |
114 |
|
|
to "protecting" paths in perls C<open>). |
115 |
|
|
|
116 |
|
|
Since commands send I<to> F<mpv> are send in UTF-8, we need to escape the |
117 |
|
|
filename (which might be in any encoding) using the C<esscape_binary> |
118 |
|
|
method - this is not needed if your filenames are just ascii, or magically |
119 |
|
|
get interpreted correctly, but if you accept arbitrary filenamews (e.g. |
120 |
|
|
from the user), you need to do this. |
121 |
|
|
|
122 |
|
|
The C<cmd_recv> method then queues the command, waits for a reply and |
123 |
|
|
returns the reply data (or croaks on error). F<mpv> would, at this point, |
124 |
|
|
load the file and, if everything was successful, show the first frame and |
125 |
|
|
pause. Note that, since F<mpv> is implement rather synchronously itself, |
126 |
|
|
do not expect commands to fail in many circumstances - for example, fit |
127 |
|
|
he file does not exit, you will likely get an event, but the C<loadfile> |
128 |
|
|
command itself will run successfully. |
129 |
|
|
|
130 |
|
|
To unpause, we send another command, C<set>, to set the C<pause> property |
131 |
|
|
to C<no>, this time using the C<cmd> method, which queues the command, but |
132 |
|
|
instead of waiting for a reply, it immediately returns a condvar that cna |
133 |
|
|
be used to receive results. |
134 |
|
|
|
135 |
|
|
This should then cause F<mpv> to start playing the video. |
136 |
|
|
|
137 |
|
|
It then again waits two seconds and quits. |
138 |
|
|
|
139 |
|
|
Now, just waiting two seconds is rather, eh, unuseful, so let's look at |
140 |
|
|
receiving events (using a somewhat embellished example): |
141 |
|
|
|
142 |
|
|
use AnyEvent; |
143 |
|
|
use AnyEvent::MPV; |
144 |
|
|
|
145 |
|
|
my $videofile = "xyzzy.mp4"; |
146 |
|
|
|
147 |
|
|
my $quit = AE::cv; |
148 |
|
|
|
149 |
|
|
my $mpv = AnyEvent::MPV->new ( |
150 |
|
|
trace => 1, |
151 |
|
|
args => ["--pause", "--idle=yes"], |
152 |
|
|
on_event => sub { |
153 |
|
|
my ($mpv, $event, $data) = @_; |
154 |
|
|
|
155 |
|
|
if ($event eq "start-file") { |
156 |
|
|
$mpv->cmd ("set", "pause", "no"); |
157 |
|
|
} elsif ($event eq "end-file") { |
158 |
|
|
print "end-file<$data->{reason}>\n"; |
159 |
|
|
$quit->send; |
160 |
|
|
} |
161 |
|
|
}, |
162 |
|
|
); |
163 |
|
|
|
164 |
|
|
$mpv->start; |
165 |
|
|
$mpv->cmd (loadfile => $mpv->escape_binary ($videofile)); |
166 |
|
|
|
167 |
|
|
$quit->recv; |
168 |
|
|
|
169 |
|
|
This example uses a global condvar C<$quit> to wait for the file to finish |
170 |
|
|
playing. Also, most of the logic is now in an C<on_event> callback, which |
171 |
|
|
receives an event name and the actual event object. |
172 |
|
|
|
173 |
|
|
The two events we handle are C<start-file>, which is emitted by F<mpv> |
174 |
|
|
once it has loaded a new file, and C<end-file>, which signals the end |
175 |
|
|
of a file. |
176 |
|
|
|
177 |
|
|
In the former event, we again set the C<pause> property to C<no> so the |
178 |
|
|
movie starts playing. For the latter event, we tell the main program to |
179 |
|
|
quit by invoking C<$quit>. |
180 |
|
|
|
181 |
|
|
This should conclude the basics of operation. There are a few more |
182 |
|
|
examples later in the documentation. |
183 |
|
|
|
184 |
|
|
=head2 ENCODING CONVENTIONS |
185 |
|
|
|
186 |
|
|
As a rule of thumb, all data you pass to this module to be sent to F<mpv> |
187 |
|
|
is expected to be in unicode. To pass something that isn't, you need to |
188 |
|
|
escape it using C<escape_binary>. |
189 |
|
|
|
190 |
|
|
Data received from C<$mpv>, however, is I<not> decoded to unicode, as data |
191 |
|
|
returned by F<mpv> is not generally encoded in unicode, and the encoding |
192 |
|
|
is usually unspecified. So if you receive data and expect it to be in |
193 |
|
|
unicode, you need to first decode it from UTF-8, but note that this might |
194 |
|
|
fail. This is not a limitation of this module - F<mpv> simply does not |
195 |
|
|
specify nor guarantee a specific encoding, or any encoding at all, in its |
196 |
|
|
protocol. |
197 |
|
|
|
198 |
|
|
=head2 METHODS |
199 |
|
|
|
200 |
|
|
=over |
201 |
root |
1.1 |
|
202 |
|
|
=cut |
203 |
|
|
|
204 |
|
|
package AnyEvent::MPV; |
205 |
|
|
|
206 |
|
|
use common::sense; |
207 |
|
|
|
208 |
root |
1.2 |
use Fcntl (); |
209 |
|
|
use Scalar::Util (); |
210 |
|
|
|
211 |
root |
1.1 |
use AnyEvent (); |
212 |
|
|
use AnyEvent::Util (); |
213 |
|
|
|
214 |
root |
1.10 |
our $VERSION = '0.2'; |
215 |
root |
1.9 |
|
216 |
|
|
sub OBSID() { 0x10000000000000 } # 2**52 |
217 |
|
|
|
218 |
root |
1.1 |
our $JSON = eval { require JSON::XS; JSON::XS:: } |
219 |
|
|
|| do { require JSON::PP; JSON::PP:: }; |
220 |
|
|
|
221 |
root |
1.5 |
our $JSON_CODER = |
222 |
|
|
|
223 |
root |
1.1 |
our $mpv_path; # last mpv path used |
224 |
|
|
our $mpv_optionlist; # output of mpv --list-options |
225 |
|
|
|
226 |
root |
1.5 |
=item $mpv = AnyEvent::MPV->new (key => value...) |
227 |
|
|
|
228 |
|
|
Creates a new C<mpv> object, but does not yet do anything. The support key-value pairs are: |
229 |
|
|
|
230 |
|
|
=over |
231 |
|
|
|
232 |
|
|
=item mpv => $path |
233 |
|
|
|
234 |
|
|
The path to the F<mpv> binary to use - by default, C<mpv> is used and |
235 |
|
|
therefore, uses your C<PATH> to find it. |
236 |
|
|
|
237 |
|
|
=item args => [...] |
238 |
|
|
|
239 |
|
|
Arguments to pass to F<mpv>. These arguments are passed after the |
240 |
|
|
hardcoded arguments used by this module, but before the arguments passed |
241 |
|
|
ot C<start>. It does not matter whether you specify your arguments using |
242 |
|
|
this key, or in the C<start> call, but when you invoke F<mpv> multiple |
243 |
|
|
times, typically the arguments used for all invocations go here, while |
244 |
|
|
arguments used for specific invocations (e..g filenames) are passed to |
245 |
|
|
C<start>. |
246 |
|
|
|
247 |
|
|
=item trace => false|true|coderef |
248 |
|
|
|
249 |
|
|
Enables tracing if true. In trace mode, output from F<mpv> is printed to |
250 |
|
|
standard error using a C<< mpv> >> prefix, and commands sent to F<mpv> |
251 |
|
|
are printed with a C<< >mpv >> prefix. |
252 |
|
|
|
253 |
|
|
If a code reference is passed, then instead of printing to standard |
254 |
|
|
errort, this coderef is invoked with a first arfgument being either |
255 |
|
|
C<< mpv> >> or C<< >mpv >>, and the second argument being a string to |
256 |
|
|
display. The default implementation simply does this: |
257 |
|
|
|
258 |
|
|
sub { |
259 |
|
|
warn "$_[0] $_[1]\n"; |
260 |
|
|
} |
261 |
|
|
|
262 |
|
|
=item on_eof => $coderef->($mpv) |
263 |
|
|
|
264 |
|
|
=item on_event => $coderef->($mpv, $event, $data) |
265 |
|
|
|
266 |
|
|
=item on_key => $coderef->($mpv, $string) |
267 |
|
|
|
268 |
|
|
These are invoked by the default method implementation of the same name - |
269 |
|
|
see below. |
270 |
|
|
|
271 |
|
|
=back |
272 |
|
|
|
273 |
|
|
=cut |
274 |
|
|
|
275 |
root |
1.1 |
sub new { |
276 |
|
|
my ($class, %kv) = @_; |
277 |
|
|
|
278 |
|
|
bless { |
279 |
|
|
mpv => "mpv", |
280 |
|
|
args => [], |
281 |
|
|
%kv, |
282 |
|
|
}, $class |
283 |
|
|
} |
284 |
|
|
|
285 |
root |
1.5 |
=item $string = $mpv->escape_binary ($string) |
286 |
|
|
|
287 |
|
|
This module excects all command data sent to F<mpv> to be in unicode. Some |
288 |
|
|
things are not, such as filenames. To pass binary data such as filenames |
289 |
|
|
through a comamnd, you need to escape it using this method. |
290 |
|
|
|
291 |
|
|
The simplest example is a C<loadfile> command: |
292 |
|
|
|
293 |
|
|
$mpv->cmd_recv (loadfile => $mpv->escape_binary ($path)); |
294 |
|
|
|
295 |
|
|
=cut |
296 |
|
|
|
297 |
root |
1.1 |
# can be used to escape filenames |
298 |
|
|
sub escape_binary { |
299 |
|
|
shift; |
300 |
|
|
local $_ = shift; |
301 |
|
|
# we escape every "illegal" octet using U+10e5df HEX. this is later undone in cmd |
302 |
|
|
s/([\x00-\x1f\x80-\xff])/sprintf "\x{10e5df}%02x", ord $1/ge; |
303 |
|
|
$_ |
304 |
|
|
} |
305 |
|
|
|
306 |
root |
1.5 |
=item $started = $mpv->start (argument...) |
307 |
|
|
|
308 |
|
|
Starts F<mpv>, passing the given arguemnts as extra arguments to |
309 |
|
|
F<mpv>. If F<mpv> is already running, it returns false, otherwise it |
310 |
|
|
returns a true value, so you can easily start F<mpv> on demand by calling |
311 |
|
|
C<start> just before using it, and if it is already running, it will not |
312 |
|
|
be started again. |
313 |
|
|
|
314 |
|
|
The arguments passwd to F<mpv> are a set of hardcoded built-in arguments, |
315 |
|
|
followed by the arguments specified in the constructor, followed by the |
316 |
|
|
arguments passwd to this method. The built-in arguments currently are |
317 |
|
|
F<--no-input-terminal>, F<--really-quiet> (or F<--quiet> in C<trace> |
318 |
|
|
mode), and C<--input-ipc-client> (or equivalent). |
319 |
|
|
|
320 |
|
|
Some commonly used and/or even useful arguments you might want to pass are: |
321 |
|
|
|
322 |
|
|
=over |
323 |
|
|
|
324 |
|
|
=item F<--idle=yes> or F<--idle=once> to keep F<mpv> from quitting when you |
325 |
|
|
don't specify a file to play. |
326 |
|
|
|
327 |
|
|
=item F<--pause>, to keep F<mpv> from instantly starting to play a file, in case you want to |
328 |
|
|
inspect/change properties first. |
329 |
|
|
|
330 |
|
|
=item F<--force-window=no> (or similar), to keep F<mpv> from instantly opening a window, or to force it to do so. |
331 |
|
|
|
332 |
|
|
=item F<--audio-client-name=yourappname>, to make sure audio streams are associated witht eh right program. |
333 |
|
|
|
334 |
|
|
=item F<--wid=id>, to embed F<mpv> into another application. |
335 |
|
|
|
336 |
|
|
=item F<--no-terminal>, F<--no-input-default-bindings>, F<--no-input-cursor>, F<--input-conf=/dev/null>, F<--input-vo-keyboard=no> - to ensure only you control input. |
337 |
|
|
|
338 |
|
|
=back |
339 |
|
|
|
340 |
|
|
The return value can be used to decide whether F<mpv> needs initializing: |
341 |
|
|
|
342 |
|
|
if ($mpv->start) { |
343 |
|
|
$mpv->bind_key (...); |
344 |
|
|
$mpv->cmd (set => property => value); |
345 |
|
|
... |
346 |
|
|
} |
347 |
|
|
|
348 |
|
|
You can immediately starting sending commands when this method returns, |
349 |
|
|
even if F<mpv> has not yet started. |
350 |
|
|
|
351 |
|
|
=cut |
352 |
|
|
|
353 |
root |
1.1 |
sub start { |
354 |
|
|
my ($self, @extra_args) = @_; |
355 |
|
|
|
356 |
root |
1.4 |
return 0 if $self->{fh}; |
357 |
root |
1.1 |
|
358 |
root |
1.9 |
$self->{obscb} = {}; |
359 |
|
|
|
360 |
root |
1.1 |
# cache optionlist for same "path" |
361 |
root |
1.2 |
($mpv_path, $mpv_optionlist) = ($self->{mpv}, scalar qx{\Q$self->{mpv}\E --list-options}) |
362 |
root |
1.1 |
if $self->{mpv} ne $mpv_path; |
363 |
|
|
|
364 |
|
|
my $options = $mpv_optionlist; |
365 |
|
|
|
366 |
|
|
my ($fh, $slave) = AnyEvent::Util::portable_socketpair |
367 |
|
|
or die "socketpair: $!\n"; |
368 |
|
|
|
369 |
root |
1.2 |
AnyEvent::Util::fh_nonblocking $fh, 1; |
370 |
root |
1.1 |
|
371 |
root |
1.2 |
$self->{pid} = fork; |
372 |
root |
1.1 |
|
373 |
|
|
if ($self->{pid} eq 0) { |
374 |
root |
1.2 |
AnyEvent::Util::fh_nonblocking $slave, 0; |
375 |
|
|
fcntl $slave, Fcntl::F_SETFD, 0; |
376 |
|
|
|
377 |
root |
1.1 |
my $input_file = $options =~ /\s--input-ipc-client\s/ ? "input-ipc-client" : "input-file"; |
378 |
|
|
|
379 |
|
|
exec $self->{mpv}, |
380 |
root |
1.5 |
qw(--no-input-terminal), |
381 |
root |
1.1 |
($self->{trace} ? "--quiet" : "--really-quiet"), |
382 |
|
|
"--$input_file=fd://" . (fileno $slave), |
383 |
|
|
@{ $self->{args} }, |
384 |
|
|
@extra_args; |
385 |
|
|
exit 1; |
386 |
|
|
} |
387 |
|
|
|
388 |
|
|
$self->{fh} = $fh; |
389 |
|
|
|
390 |
root |
1.2 |
my $trace = delete $self->{trace} || sub { }; |
391 |
root |
1.1 |
|
392 |
|
|
$trace = sub { warn "$_[0] $_[1]\n" } if $trace && !ref $trace; |
393 |
|
|
|
394 |
|
|
my $buf; |
395 |
root |
1.2 |
|
396 |
|
|
Scalar::Util::weaken $self; |
397 |
root |
1.1 |
|
398 |
|
|
$self->{rw} = AE::io $fh, 0, sub { |
399 |
|
|
if (sysread $fh, $buf, 8192, length $buf) { |
400 |
|
|
while ($buf =~ s/^([^\n]+)\n//) { |
401 |
|
|
$trace->("mpv>" => "$1"); |
402 |
|
|
|
403 |
|
|
if ("{" eq substr $1, 0, 1) { |
404 |
|
|
eval { |
405 |
root |
1.5 |
my $reply = $JSON->new->latin1->decode ($1); |
406 |
root |
1.1 |
|
407 |
|
|
if (exists $reply->{event}) { |
408 |
|
|
if ( |
409 |
|
|
$reply->{event} eq "client-message" |
410 |
|
|
and $reply->{args}[0] eq "AnyEvent::MPV" |
411 |
|
|
) { |
412 |
root |
1.3 |
if ($reply->{args}[1] eq "key") { |
413 |
root |
1.4 |
(my $key = $reply->{args}[2]) =~ s/\\x(..)/chr hex $1/ge; |
414 |
|
|
$self->on_key ($key); |
415 |
root |
1.3 |
} |
416 |
root |
1.9 |
} elsif ( |
417 |
|
|
$reply->{event} eq "property-change" |
418 |
|
|
and OBSID <= $reply->{id} |
419 |
|
|
) { |
420 |
|
|
if (my $cb = $self->{obscb}{$reply->{id}}) { |
421 |
|
|
$cb->($self, $reply->{name}, $reply->{data}); |
422 |
|
|
} |
423 |
root |
1.1 |
} else { |
424 |
root |
1.7 |
$self->on_event (delete $reply->{event}, $reply); |
425 |
root |
1.1 |
} |
426 |
|
|
} elsif (exists $reply->{request_id}) { |
427 |
root |
1.9 |
my $cv = delete $self->{cmdcv}{$reply->{request_id}}; |
428 |
root |
1.1 |
|
429 |
|
|
unless ($cv) { |
430 |
|
|
warn "no cv found for request id <$reply->{request_id}>\n"; |
431 |
|
|
next; |
432 |
|
|
} |
433 |
|
|
|
434 |
|
|
if (exists $reply->{data}) { |
435 |
|
|
$cv->send ($reply->{data}); |
436 |
|
|
} elsif ($reply->{error} eq "success") { # success means error... eh.. no... |
437 |
|
|
$cv->send; |
438 |
|
|
} else { |
439 |
|
|
$cv->croak ($reply->{error}); |
440 |
|
|
} |
441 |
|
|
|
442 |
|
|
} else { |
443 |
|
|
warn "unexpected reply from mpv, pleasew report: <$1>\n"; |
444 |
|
|
} |
445 |
|
|
}; |
446 |
|
|
warn $@ if $@; |
447 |
|
|
} else { |
448 |
|
|
$trace->("mpv>" => "$1"); |
449 |
|
|
} |
450 |
|
|
} |
451 |
|
|
} else { |
452 |
root |
1.2 |
$self->stop; |
453 |
root |
1.1 |
$self->on_eof; |
454 |
|
|
} |
455 |
|
|
}; |
456 |
|
|
|
457 |
root |
1.8 |
my $wbuf; |
458 |
|
|
my $reqid; |
459 |
|
|
|
460 |
|
|
$self->{_cmd} = sub { |
461 |
|
|
my $cv = AE::cv; |
462 |
|
|
|
463 |
root |
1.9 |
$self->{cmdcv}{++$reqid} = $cv; |
464 |
root |
1.8 |
|
465 |
|
|
my $cmd = $JSON->new->utf8->encode ({ command => ref $_[0] ? $_[0] : \@_, request_id => $reqid*1 }); |
466 |
|
|
|
467 |
|
|
# (un-)apply escape_binary hack |
468 |
|
|
$cmd =~ s/\xf4\x8e\x97\x9f(..)/sprintf sprintf "\\x%02x", hex $1/ges; # f48e979f == 10e5df in utf-8 |
469 |
|
|
|
470 |
root |
1.9 |
$trace->(">mpv" => $cmd); |
471 |
|
|
|
472 |
root |
1.8 |
$wbuf .= "$cmd\n"; |
473 |
root |
1.1 |
|
474 |
|
|
$self->{ww} ||= AE::io $fh, 1, sub { |
475 |
|
|
my $len = syswrite $fh, $wbuf; |
476 |
|
|
substr $wbuf, 0, $len, ""; |
477 |
|
|
undef $self->{ww} unless length $wbuf; |
478 |
|
|
}; |
479 |
root |
1.8 |
|
480 |
|
|
$cv |
481 |
root |
1.1 |
}; |
482 |
root |
1.4 |
|
483 |
|
|
1 |
484 |
root |
1.1 |
} |
485 |
|
|
|
486 |
root |
1.8 |
sub DESTROY { |
487 |
|
|
$_[0]->stop; |
488 |
|
|
} |
489 |
|
|
|
490 |
root |
1.5 |
=item $mpv->stop |
491 |
|
|
|
492 |
|
|
Ensures that F<mpv> is being stopped, by killing F<mpv> with a C<TERM> |
493 |
|
|
signal if needed. After this, you can C<< ->start >> a new instance again. |
494 |
|
|
|
495 |
|
|
=cut |
496 |
|
|
|
497 |
|
|
sub stop { |
498 |
|
|
my ($self) = @_; |
499 |
|
|
|
500 |
|
|
delete $self->{rw}; |
501 |
|
|
delete $self->{ww}; |
502 |
|
|
|
503 |
|
|
if ($self->{pid}) { |
504 |
|
|
|
505 |
|
|
close delete $self->{fh}; # current mpv versions should cleanup on their own on close |
506 |
|
|
|
507 |
|
|
kill TERM => $self->{pid}; |
508 |
|
|
|
509 |
|
|
} |
510 |
|
|
|
511 |
|
|
delete $self->{pid}; |
512 |
root |
1.9 |
delete $self->{cmdcv}; |
513 |
root |
1.8 |
delete $self->{obsid}; |
514 |
root |
1.9 |
delete $self->{obscb}; |
515 |
root |
1.8 |
delete $self->{wbuf}; |
516 |
root |
1.5 |
} |
517 |
|
|
|
518 |
|
|
=item $mpv->on_eof |
519 |
|
|
|
520 |
|
|
This method is called when F<mpv> quits - usually unexpectedly. The |
521 |
|
|
default implementation will call the C<on_eof> code reference specified in |
522 |
|
|
the constructor, or do nothing if none was given. |
523 |
|
|
|
524 |
|
|
For subclassing, see I<SUBCLASSING>, below. |
525 |
|
|
|
526 |
|
|
=cut |
527 |
|
|
|
528 |
root |
1.1 |
sub on_eof { |
529 |
|
|
my ($self) = @_; |
530 |
|
|
|
531 |
|
|
$self->{on_eof}($self) if $self->{on_eof}; |
532 |
|
|
} |
533 |
|
|
|
534 |
root |
1.5 |
=item $mpv->on_event ($event, $data) |
535 |
|
|
|
536 |
|
|
This method is called when F<mpv> sends an asynchronous event. The default |
537 |
|
|
implementation will call the C<on_event> code reference specified in the |
538 |
|
|
constructor, or do nothing if none was given. |
539 |
|
|
|
540 |
root |
1.7 |
The first/implicit argument is the C<$mpv> object, the second is the |
541 |
|
|
event name (same as C<< $data->{event} >>, purely for convenience), and |
542 |
|
|
the third argument is the event object as sent by F<mpv> (sans C<event> |
543 |
|
|
key). See L<List of events|https://mpv.io/manual/stable/#list-of-events> |
544 |
|
|
in its documentation. |
545 |
root |
1.5 |
|
546 |
|
|
For subclassing, see I<SUBCLASSING>, below. |
547 |
|
|
|
548 |
|
|
=cut |
549 |
|
|
|
550 |
root |
1.1 |
sub on_event { |
551 |
|
|
my ($self, $key) = @_; |
552 |
|
|
|
553 |
|
|
$self->{on_event}($self, $key) if $self->{on_event}; |
554 |
|
|
} |
555 |
|
|
|
556 |
root |
1.5 |
=item $mpv->on_key ($string) |
557 |
|
|
|
558 |
|
|
Invoked when a key declared by C<< ->bind_key >> is pressed. The default |
559 |
|
|
invokes the C<on_key> code reference specified in the constructor with the |
560 |
|
|
C<$mpv> object and the key name as arguments, or do nothing if none was |
561 |
|
|
given. |
562 |
|
|
|
563 |
|
|
For more details and examples, see the C<bind_key> method. |
564 |
|
|
|
565 |
|
|
For subclassing, see I<SUBCLASSING>, below. |
566 |
|
|
|
567 |
|
|
=cut |
568 |
|
|
|
569 |
root |
1.2 |
sub on_key { |
570 |
root |
1.1 |
my ($self, $key) = @_; |
571 |
|
|
|
572 |
root |
1.2 |
$self->{on_key}($self, $key) if $self->{on_key}; |
573 |
root |
1.1 |
} |
574 |
|
|
|
575 |
root |
1.5 |
=item $mpv->cmd ($command => $arg, $arg...) |
576 |
|
|
|
577 |
|
|
Queues a command to be sent to F<mpv>, using the given arguments, and |
578 |
|
|
immediately return a condvar. |
579 |
|
|
|
580 |
|
|
See L<the mpv |
581 |
|
|
documentation|https://mpv.io/manual/stable/#list-of-input-commands> for |
582 |
|
|
details on individual commands. |
583 |
|
|
|
584 |
|
|
The condvar can be ignored: |
585 |
|
|
|
586 |
|
|
$mpv->cmd (set_property => "deinterlace", "yes"); |
587 |
|
|
|
588 |
|
|
Or it can be used to synchronously wait for the command results: |
589 |
|
|
|
590 |
|
|
$cv = $mpv->cmd (get_property => "video-format"); |
591 |
|
|
$format = $cv->recv; |
592 |
|
|
|
593 |
|
|
# or simpler: |
594 |
|
|
|
595 |
|
|
$format = $mpv->cmd (get_property => "video-format")->recv; |
596 |
|
|
|
597 |
|
|
# or even simpler: |
598 |
|
|
|
599 |
|
|
$format = $mpv->cmd_recv (get_property => "video-format"); |
600 |
|
|
|
601 |
|
|
Or you can set a callback: |
602 |
|
|
|
603 |
|
|
$cv = $mpv->cmd (get_property => "video-format"); |
604 |
|
|
$cv->cb (sub { |
605 |
|
|
my $format = $_[0]->recv; |
606 |
|
|
}); |
607 |
|
|
|
608 |
|
|
On error, the condvar will croak when C<recv> is called. |
609 |
|
|
|
610 |
|
|
=cut |
611 |
|
|
|
612 |
root |
1.1 |
sub cmd { |
613 |
root |
1.8 |
my $self = shift; |
614 |
root |
1.1 |
|
615 |
root |
1.8 |
$self->{_cmd}->(@_) |
616 |
root |
1.1 |
} |
617 |
|
|
|
618 |
root |
1.5 |
=item $result = $mpv->cmd_recv ($command => $arg, $arg...) |
619 |
|
|
|
620 |
|
|
The same as calling C<cmd> and immediately C<recv> on its return |
621 |
|
|
value. Useful when you don't want to mess with F<mpv> asynchronously or |
622 |
|
|
simply needs to have the result: |
623 |
|
|
|
624 |
|
|
$mpv->cmd_recv ("stop"); |
625 |
|
|
$position = $mpv->cmd_recv ("get_property", "playback-time"); |
626 |
|
|
|
627 |
|
|
=cut |
628 |
|
|
|
629 |
root |
1.4 |
sub cmd_recv { |
630 |
|
|
&cmd->recv |
631 |
|
|
} |
632 |
|
|
|
633 |
root |
1.5 |
=item $mpv->bind_key ($INPUT => $string) |
634 |
|
|
|
635 |
|
|
This is an extension implement by this module to make it easy to get key events. The way this is implemented |
636 |
|
|
is to bind a C<client-message> witha first argument of C<AnyEvent::MPV> and the C<$string> you passed. This C<$string> is then |
637 |
root |
1.9 |
passed to the C<on_key> handle when the key is proessed, e.g.: |
638 |
root |
1.5 |
|
639 |
|
|
my $mpv = AnyEvent::MPV->new ( |
640 |
|
|
on_key => sub { |
641 |
|
|
my ($mpv, $key) = @_; |
642 |
|
|
|
643 |
|
|
if ($key eq "letmeout") { |
644 |
|
|
print "user pressed escape\n"; |
645 |
|
|
} |
646 |
|
|
}, |
647 |
|
|
); |
648 |
|
|
|
649 |
|
|
$mpv_>bind_key (ESC => "letmeout"); |
650 |
|
|
|
651 |
|
|
The key configuration is lost when F<mpv> is stopped and must be (re-)done |
652 |
|
|
after every C<start>. |
653 |
|
|
|
654 |
|
|
=cut |
655 |
|
|
|
656 |
root |
1.4 |
sub bind_key { |
657 |
|
|
my ($self, $key, $event) = @_; |
658 |
|
|
|
659 |
|
|
$event =~ s/([^A-Za-z0-9\-_])/sprintf "\\x%02x", ord $1/ge; |
660 |
|
|
$self->cmd (keybind => $key => "no-osd script-message AnyEvent::MPV key $event"); |
661 |
|
|
} |
662 |
|
|
|
663 |
root |
1.9 |
sub AnyEvent::MPV::Unobserve::DESTROY { |
664 |
|
|
my ($mpv, $obscb, $obsid) = @{$_[0]}; |
665 |
|
|
|
666 |
|
|
delete $obscb->{$obsid}; |
667 |
|
|
|
668 |
|
|
if ($obscb == $mpv->{obscb}) { |
669 |
|
|
$mpv->cmd (unobserve_property => $obsid+0); |
670 |
|
|
} |
671 |
|
|
} |
672 |
|
|
|
673 |
|
|
=item [$guard] = $mpv->observe_property ($name => $coderef->($mpv, $name, $value)) |
674 |
|
|
|
675 |
|
|
=item [$guard] = $mpv->observe_property_string ($name => $coderef->($mpv, $name, $value)) |
676 |
|
|
|
677 |
|
|
These methods wrap a registry system around F<mpv>'s C<observe_property> |
678 |
|
|
and C<observe_property_string> commands - every time the named property |
679 |
|
|
changes, the coderef is invoked with the C<$mpv> object, the name of the |
680 |
|
|
property and the new value. |
681 |
|
|
|
682 |
|
|
For a list of properties that you can observe, see L<the mpv |
683 |
|
|
documentation|https://mpv.io/manual/stable/#property-list>. |
684 |
|
|
|
685 |
|
|
Due to the (sane :) way F<mpv> handles these requests, you will always |
686 |
|
|
get a property cxhange event right after registering an observer (meaning |
687 |
|
|
you don't have to query the current value), and it is also possible to |
688 |
|
|
register multiple observers for the same property - they will all be |
689 |
|
|
handled properly. |
690 |
|
|
|
691 |
|
|
When called in void context, the observer stays in place until F<mpv> |
692 |
|
|
is stopped. In any otrher context, these methods return a guard |
693 |
|
|
object that, when it goes out of scope, unregisters the observe using |
694 |
|
|
C<unobserve_property>. |
695 |
|
|
|
696 |
root |
1.10 |
Internally, this method uses observer ids of 2**52 (0x10000000000000) or |
697 |
|
|
higher - it will not interfere with lower ovserver ids, so it is possible |
698 |
|
|
to completely ignore this system and execute C<observe_property> commands |
699 |
|
|
yourself, whilst listening to C<property-change> events - as long as your |
700 |
|
|
ids stay below 2**52. |
701 |
|
|
|
702 |
root |
1.9 |
Example: register observers for changtes in C<aid> and C<sid>. Note that |
703 |
|
|
a dummy statement is added to make sure the method is called in void |
704 |
|
|
context. |
705 |
|
|
|
706 |
|
|
sub register_observers { |
707 |
|
|
my ($mpv) = @_; |
708 |
|
|
|
709 |
|
|
$mpv->observe_property (aid => sub { |
710 |
|
|
my ($mpv, $name, $value) = @_; |
711 |
|
|
print "property aid (=$name) has changed to $value\n"; |
712 |
|
|
}); |
713 |
|
|
|
714 |
|
|
$mpv->observe_property (sid => sub { |
715 |
|
|
my ($mpv, $name, $value) = @_; |
716 |
|
|
print "property sid (=$name) has changed to $value\n"; |
717 |
|
|
}); |
718 |
|
|
|
719 |
|
|
() # ensure the above method is called in void context |
720 |
|
|
} |
721 |
|
|
|
722 |
|
|
=cut |
723 |
|
|
|
724 |
|
|
sub _observe_property { |
725 |
|
|
my ($self, $type, $property, $cb) = @_; |
726 |
|
|
|
727 |
|
|
my $obsid = OBSID + ++$self->{obsid}; |
728 |
|
|
$self->cmd ($type => $obsid+0, $property); |
729 |
|
|
$self->{obscb}{$obsid} = $cb; |
730 |
|
|
|
731 |
|
|
defined wantarray and do { |
732 |
|
|
my $unobserve = bless [$self, $self->{obscb}, $obsid], AnyEvent::MPV::Unobserve::; |
733 |
|
|
Scalar::Util::weaken $unobserve->[0]; |
734 |
|
|
$unobserve |
735 |
|
|
} |
736 |
|
|
} |
737 |
|
|
|
738 |
|
|
sub observe_property { |
739 |
|
|
my ($self, $property, $cb) = @_; |
740 |
|
|
|
741 |
|
|
$self->_observe_property (observe_property => $property, $cb) |
742 |
|
|
} |
743 |
|
|
|
744 |
|
|
sub observe_property_string { |
745 |
|
|
my ($self, $property, $cb) = @_; |
746 |
|
|
|
747 |
|
|
$self->_observe_property (observe_property_string => $property, $cb) |
748 |
|
|
} |
749 |
|
|
|
750 |
root |
1.5 |
=back |
751 |
root |
1.1 |
|
752 |
root |
1.5 |
=head2 SUBCLASSING |
753 |
root |
1.1 |
|
754 |
root |
1.5 |
Like most perl objects, C<AnyEvent::MPV> objects are implemented as |
755 |
|
|
hashes, with the constructor simply storing all passed key-value pairs in |
756 |
|
|
the object. If you want to subclass to provide your own C<on_*> methods, |
757 |
|
|
be my guest and rummage around in the internals as much as you wish - the |
758 |
|
|
only guarantee that this module dcoes is that it will not use keys with |
759 |
|
|
double colons in the name, so youc an use those, or chose to simply not |
760 |
|
|
care and deal with the breakage. |
761 |
root |
1.1 |
|
762 |
root |
1.5 |
If you don't want to go to the effort of subclassing this module, you can |
763 |
|
|
also specify all event handlers as constructor keys. |
764 |
root |
1.1 |
|
765 |
|
|
=head1 SEE ALSO |
766 |
|
|
|
767 |
root |
1.5 |
L<AnyEvent>, L<the mpv command documentation|https://mpv.io/manual/stable/#command-interface>. |
768 |
root |
1.1 |
|
769 |
|
|
=head1 AUTHOR |
770 |
|
|
|
771 |
|
|
Marc Lehmann <schmorp@schmorp.de> |
772 |
|
|
http://home.schmorp.de/ |
773 |
|
|
|
774 |
|
|
=cut |
775 |
|
|
|
776 |
|
|
1 |
777 |
|
|
|