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 |
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 |
and also prints the commands and responses, so you can have an idea what |
68 |
is going on. |
69 |
|
70 |
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 |
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 |
|
202 |
=cut |
203 |
|
204 |
package AnyEvent::MPV; |
205 |
|
206 |
use common::sense; |
207 |
|
208 |
use Fcntl (); |
209 |
use Scalar::Util (); |
210 |
|
211 |
use AnyEvent (); |
212 |
use AnyEvent::Util (); |
213 |
|
214 |
our $VERSION = '0.2'; |
215 |
|
216 |
sub OBSID() { 0x10000000000000 } # 2**52 |
217 |
|
218 |
our $JSON = eval { require JSON::XS; JSON::XS:: } |
219 |
|| do { require JSON::PP; JSON::PP:: }; |
220 |
|
221 |
our $JSON_CODER = |
222 |
|
223 |
our $mpv_path; # last mpv path used |
224 |
our $mpv_optionlist; # output of mpv --list-options |
225 |
|
226 |
=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 |
sub new { |
276 |
my ($class, %kv) = @_; |
277 |
|
278 |
bless { |
279 |
mpv => "mpv", |
280 |
args => [], |
281 |
%kv, |
282 |
}, $class |
283 |
} |
284 |
|
285 |
=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 |
# 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 |
=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 |
sub start { |
354 |
my ($self, @extra_args) = @_; |
355 |
|
356 |
return 0 if $self->{fh}; |
357 |
|
358 |
$self->{obscb} = {}; |
359 |
|
360 |
# cache optionlist for same "path" |
361 |
($mpv_path, $mpv_optionlist) = ($self->{mpv}, scalar qx{\Q$self->{mpv}\E --list-options}) |
362 |
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 |
AnyEvent::Util::fh_nonblocking $fh, 1; |
370 |
|
371 |
$self->{pid} = fork; |
372 |
|
373 |
if ($self->{pid} eq 0) { |
374 |
AnyEvent::Util::fh_nonblocking $slave, 0; |
375 |
fcntl $slave, Fcntl::F_SETFD, 0; |
376 |
|
377 |
my $input_file = $options =~ /\s--input-ipc-client\s/ ? "input-ipc-client" : "input-file"; |
378 |
|
379 |
exec $self->{mpv}, |
380 |
qw(--no-input-terminal), |
381 |
($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 |
my $trace = delete $self->{trace} || sub { }; |
391 |
|
392 |
$trace = sub { warn "$_[0] $_[1]\n" } if $trace && !ref $trace; |
393 |
|
394 |
my $buf; |
395 |
|
396 |
Scalar::Util::weaken $self; |
397 |
|
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 |
my $reply = $JSON->new->latin1->decode ($1); |
406 |
|
407 |
if (exists $reply->{event}) { |
408 |
if ( |
409 |
$reply->{event} eq "client-message" |
410 |
and $reply->{args}[0] eq "AnyEvent::MPV" |
411 |
) { |
412 |
if ($reply->{args}[1] eq "key") { |
413 |
(my $key = $reply->{args}[2]) =~ s/\\x(..)/chr hex $1/ge; |
414 |
$self->on_key ($key); |
415 |
} |
416 |
} 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 |
} else { |
424 |
$self->on_event (delete $reply->{event}, $reply); |
425 |
} |
426 |
} elsif (exists $reply->{request_id}) { |
427 |
my $cv = delete $self->{cmdcv}{$reply->{request_id}}; |
428 |
|
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 |
$self->stop; |
453 |
$self->on_eof; |
454 |
} |
455 |
}; |
456 |
|
457 |
my $wbuf; |
458 |
my $reqid; |
459 |
|
460 |
$self->{_cmd} = sub { |
461 |
my $cv = AE::cv; |
462 |
|
463 |
$self->{cmdcv}{++$reqid} = $cv; |
464 |
|
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 |
$trace->(">mpv" => $cmd); |
471 |
|
472 |
$wbuf .= "$cmd\n"; |
473 |
|
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 |
|
480 |
$cv |
481 |
}; |
482 |
|
483 |
1 |
484 |
} |
485 |
|
486 |
sub DESTROY { |
487 |
$_[0]->stop; |
488 |
} |
489 |
|
490 |
=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 |
delete $self->{cmdcv}; |
513 |
delete $self->{obsid}; |
514 |
delete $self->{obscb}; |
515 |
delete $self->{wbuf}; |
516 |
} |
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 |
sub on_eof { |
529 |
my ($self) = @_; |
530 |
|
531 |
$self->{on_eof}($self) if $self->{on_eof}; |
532 |
} |
533 |
|
534 |
=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 |
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 |
|
546 |
For subclassing, see I<SUBCLASSING>, below. |
547 |
|
548 |
=cut |
549 |
|
550 |
sub on_event { |
551 |
my ($self, $key) = @_; |
552 |
|
553 |
$self->{on_event}($self, $key) if $self->{on_event}; |
554 |
} |
555 |
|
556 |
=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 |
sub on_key { |
570 |
my ($self, $key) = @_; |
571 |
|
572 |
$self->{on_key}($self, $key) if $self->{on_key}; |
573 |
} |
574 |
|
575 |
=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 |
sub cmd { |
613 |
my $self = shift; |
614 |
|
615 |
$self->{_cmd}->(@_) |
616 |
} |
617 |
|
618 |
=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 |
sub cmd_recv { |
630 |
&cmd->recv |
631 |
} |
632 |
|
633 |
=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 |
passed to the C<on_key> handle when the key is proessed, e.g.: |
638 |
|
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 |
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 |
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 |
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 |
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 |
=back |
751 |
|
752 |
=head2 SUBCLASSING |
753 |
|
754 |
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 |
|
762 |
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 |
|
765 |
=head1 SEE ALSO |
766 |
|
767 |
L<AnyEvent>, L<the mpv command documentation|https://mpv.io/manual/stable/#command-interface>. |
768 |
|
769 |
=head1 AUTHOR |
770 |
|
771 |
Marc Lehmann <schmorp@schmorp.de> |
772 |
http://home.schmorp.de/ |
773 |
|
774 |
=cut |
775 |
|
776 |
1 |
777 |
|