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 |
); |
153 |
|
154 |
$mpv->start; |
155 |
|
156 |
$mpv->register_event (start_file => sub { |
157 |
$mpv->cmd ("set", "pause", "no"); |
158 |
}); |
159 |
|
160 |
$mpv->register_event (end_file => sub { |
161 |
my ($mpv, $event, $data) = @_; |
162 |
|
163 |
print "end-file<$data->{reason}>\n"; |
164 |
$quit->send; |
165 |
}); |
166 |
|
167 |
$mpv->cmd (loadfile => $mpv->escape_binary ($videofile)); |
168 |
|
169 |
$quit->recv; |
170 |
|
171 |
This example uses a global condvar C<$quit> to wait for the file to finish |
172 |
playing. Also, most of the logic is now implement in event handlers. |
173 |
|
174 |
The two events handlers we register are C<start-file>, which is emitted by |
175 |
F<mpv> once it has loaded a new file, and C<end-file>, which signals the |
176 |
end of a file (underscores are internally replaced by minus signs, so you |
177 |
cna speicfy event names with either). |
178 |
|
179 |
In the C<start-file> event, we again set the C<pause> property to C<no> |
180 |
so the movie starts playing. For the C<end-file> event, we tell the main |
181 |
program to quit by invoking C<$quit>. |
182 |
|
183 |
This should conclude the basics of operation. There are a few more |
184 |
examples later in the documentation. |
185 |
|
186 |
=head2 ENCODING CONVENTIONS |
187 |
|
188 |
As a rule of thumb, all data you pass to this module to be sent to F<mpv> |
189 |
is expected to be in unicode. To pass something that isn't, you need to |
190 |
escape it using C<escape_binary>. |
191 |
|
192 |
Data received from C<$mpv>, however, is I<not> decoded to unicode, as data |
193 |
returned by F<mpv> is not generally encoded in unicode, and the encoding |
194 |
is usually unspecified. So if you receive data and expect it to be in |
195 |
unicode, you need to first decode it from UTF-8, but note that this might |
196 |
fail. This is not a limitation of this module - F<mpv> simply does not |
197 |
specify nor guarantee a specific encoding, or any encoding at all, in its |
198 |
protocol. |
199 |
|
200 |
=head2 METHODS |
201 |
|
202 |
=over |
203 |
|
204 |
=cut |
205 |
|
206 |
package AnyEvent::MPV; |
207 |
|
208 |
use common::sense; |
209 |
|
210 |
use Fcntl (); |
211 |
use Scalar::Util (); |
212 |
|
213 |
use AnyEvent (); |
214 |
use AnyEvent::Util (); |
215 |
|
216 |
our $VERSION = '0.2'; |
217 |
|
218 |
sub OBSID() { 0x10000000000000 } # 2**52 |
219 |
|
220 |
our $JSON = eval { require JSON::XS; JSON::XS:: } |
221 |
|| do { require JSON::PP; JSON::PP:: }; |
222 |
|
223 |
our $JSON_ENCODER = $JSON->new->utf8; |
224 |
our $JSON_DECODER = $JSON->new->latin1; |
225 |
|
226 |
our $mpv_path; # last mpv path used |
227 |
our $mpv_optionlist; # output of mpv --list-options |
228 |
|
229 |
=item $mpv = AnyEvent::MPV->new (key => value...) |
230 |
|
231 |
Creates a new C<mpv> object, but does not yet do anything. The support key-value pairs are: |
232 |
|
233 |
=over |
234 |
|
235 |
=item mpv => $path |
236 |
|
237 |
The path to the F<mpv> binary to use - by default, C<mpv> is used and |
238 |
therefore, uses your C<PATH> to find it. |
239 |
|
240 |
=item args => [...] |
241 |
|
242 |
Arguments to pass to F<mpv>. These arguments are passed after the |
243 |
hardcoded arguments used by this module, but before the arguments passed |
244 |
ot C<start>. It does not matter whether you specify your arguments using |
245 |
this key, or in the C<start> call, but when you invoke F<mpv> multiple |
246 |
times, typically the arguments used for all invocations go here, while |
247 |
arguments used for specific invocations (e..g filenames) are passed to |
248 |
C<start>. |
249 |
|
250 |
=item trace => false|true|coderef |
251 |
|
252 |
Enables tracing if true. In trace mode, output from F<mpv> is printed to |
253 |
standard error using a C<< mpv> >> prefix, and commands sent to F<mpv> |
254 |
are printed with a C<< >mpv >> prefix. |
255 |
|
256 |
If a code reference is passed, then instead of printing to standard |
257 |
errort, this coderef is invoked with a first arfgument being either |
258 |
C<< mpv> >> or C<< >mpv >>, and the second argument being a string to |
259 |
display. The default implementation simply does this: |
260 |
|
261 |
sub { |
262 |
warn "$_[0] $_[1]\n"; |
263 |
} |
264 |
|
265 |
=item on_eof => $coderef->($mpv) |
266 |
|
267 |
=item on_event => $coderef->($mpv, $event, $data) |
268 |
|
269 |
=item on_key => $coderef->($mpv, $string) |
270 |
|
271 |
These are invoked by the default method implementation of the same name - |
272 |
see below. |
273 |
|
274 |
=back |
275 |
|
276 |
=cut |
277 |
|
278 |
sub new { |
279 |
my ($class, %kv) = @_; |
280 |
|
281 |
bless { |
282 |
mpv => "mpv", |
283 |
args => [], |
284 |
%kv, |
285 |
}, $class |
286 |
} |
287 |
|
288 |
=item $string = $mpv->escape_binary ($string) |
289 |
|
290 |
This module excects all command data sent to F<mpv> to be in unicode. Some |
291 |
things are not, such as filenames. To pass binary data such as filenames |
292 |
through a comamnd, you need to escape it using this method. |
293 |
|
294 |
The simplest example is a C<loadfile> command: |
295 |
|
296 |
$mpv->cmd_recv (loadfile => $mpv->escape_binary ($path)); |
297 |
|
298 |
=cut |
299 |
|
300 |
# can be used to escape filenames |
301 |
sub escape_binary { |
302 |
shift; |
303 |
local $_ = shift; |
304 |
# we escape every "illegal" octet using U+10e5df HEX. this is later undone in cmd |
305 |
s/([\x00-\x1f\x80-\xff])/sprintf "\x{10e5df}%02x", ord $1/ge; |
306 |
$_ |
307 |
} |
308 |
|
309 |
=item $started = $mpv->start (argument...) |
310 |
|
311 |
Starts F<mpv>, passing the given arguemnts as extra arguments to |
312 |
F<mpv>. If F<mpv> is already running, it returns false, otherwise it |
313 |
returns a true value, so you can easily start F<mpv> on demand by calling |
314 |
C<start> just before using it, and if it is already running, it will not |
315 |
be started again. |
316 |
|
317 |
The arguments passwd to F<mpv> are a set of hardcoded built-in arguments, |
318 |
followed by the arguments specified in the constructor, followed by the |
319 |
arguments passwd to this method. The built-in arguments currently are |
320 |
F<--no-input-terminal>, F<--really-quiet> (or F<--quiet> in C<trace> |
321 |
mode), and C<--input-ipc-client> (or equivalent). |
322 |
|
323 |
Some commonly used and/or even useful arguments you might want to pass are: |
324 |
|
325 |
=over |
326 |
|
327 |
=item F<--idle=yes> or F<--idle=once> to keep F<mpv> from quitting when you |
328 |
don't specify a file to play. |
329 |
|
330 |
=item F<--pause>, to keep F<mpv> from instantly starting to play a file, in case you want to |
331 |
inspect/change properties first. |
332 |
|
333 |
=item F<--force-window=no> (or similar), to keep F<mpv> from instantly opening a window, or to force it to do so. |
334 |
|
335 |
=item F<--audio-client-name=yourappname>, to make sure audio streams are associated witht eh right program. |
336 |
|
337 |
=item F<--wid=id>, to embed F<mpv> into another application. |
338 |
|
339 |
=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. |
340 |
|
341 |
=back |
342 |
|
343 |
The return value can be used to decide whether F<mpv> needs initializing: |
344 |
|
345 |
if ($mpv->start) { |
346 |
$mpv->bind_key (...); |
347 |
$mpv->cmd (set => property => value); |
348 |
... |
349 |
} |
350 |
|
351 |
You can immediately starting sending commands when this method returns, |
352 |
even if F<mpv> has not yet started. |
353 |
|
354 |
=cut |
355 |
|
356 |
sub start { |
357 |
my ($self, @extra_args) = @_; |
358 |
|
359 |
return 0 if $self->{fh}; |
360 |
|
361 |
# cache optionlist for same "path" |
362 |
($mpv_path, $mpv_optionlist) = ($self->{mpv}, scalar qx{\Q$self->{mpv}\E --list-options}) |
363 |
if $self->{mpv} ne $mpv_path; |
364 |
|
365 |
my $options = $mpv_optionlist; |
366 |
|
367 |
my ($fh, $slave) = AnyEvent::Util::portable_socketpair |
368 |
or die "socketpair: $!\n"; |
369 |
|
370 |
AnyEvent::Util::fh_nonblocking $fh, 1; |
371 |
|
372 |
$self->{pid} = fork; |
373 |
|
374 |
if ($self->{pid} eq 0) { |
375 |
AnyEvent::Util::fh_nonblocking $slave, 0; |
376 |
fcntl $slave, Fcntl::F_SETFD, 0; |
377 |
|
378 |
my $input_file = $options =~ /\s--input-ipc-client\s/ ? "input-ipc-client" : "input-file"; |
379 |
|
380 |
exec $self->{mpv}, |
381 |
qw(--no-input-terminal), |
382 |
($self->{trace} ? "--quiet" : "--really-quiet"), |
383 |
"--$input_file=fd://" . (fileno $slave), |
384 |
@{ $self->{args} }, |
385 |
@extra_args; |
386 |
exit 1; |
387 |
} |
388 |
|
389 |
$self->{fh} = $fh; |
390 |
|
391 |
my $trace = delete $self->{trace} || sub { }; |
392 |
|
393 |
$trace = sub { warn "$_[0] $_[1]\n" } if $trace && !ref $trace; |
394 |
|
395 |
my $buf; |
396 |
|
397 |
Scalar::Util::weaken $self; |
398 |
|
399 |
$self->{rw} = AE::io $fh, 0, sub { |
400 |
if (sysread $fh, $buf, 8192, length $buf) { |
401 |
while ($buf =~ s/^([^\n]+)\n//) { |
402 |
$trace->("mpv>" => "$1"); |
403 |
|
404 |
if ("{" eq substr $1, 0, 1) { |
405 |
eval { |
406 |
my $reply = $JSON_DECODER->decode ($1); |
407 |
|
408 |
if (defined (my $event = delete $reply->{event})) { |
409 |
if ( |
410 |
$event eq "client-message" |
411 |
and $reply->{args}[0] eq "AnyEvent::MPV" |
412 |
) { |
413 |
if ($reply->{args}[1] eq "key") { |
414 |
(my $key = $reply->{args}[2]) =~ s/\\x(..)/chr hex $1/ge; |
415 |
$self->on_key ($key); |
416 |
} |
417 |
} elsif ( |
418 |
$event eq "property-change" |
419 |
and OBSID <= $reply->{id} |
420 |
) { |
421 |
if (my $cb = $self->{obscb}{$reply->{id}}) { |
422 |
$cb->($self, $event, $reply->{data}); |
423 |
} |
424 |
} else { |
425 |
if (my $cbs = $self->{evtcb}{$event}) { |
426 |
for my $evtid (keys %$cbs) { |
427 |
my $cb = $cbs->{$evtid} |
428 |
or next; |
429 |
$cb->($self, $event, $reply); |
430 |
} |
431 |
} |
432 |
|
433 |
$self->on_event ($event, $reply); |
434 |
} |
435 |
} elsif (exists $reply->{request_id}) { |
436 |
my $cv = delete $self->{cmdcv}{$reply->{request_id}}; |
437 |
|
438 |
unless ($cv) { |
439 |
warn "no cv found for request id <$reply->{request_id}>\n"; |
440 |
next; |
441 |
} |
442 |
|
443 |
if (exists $reply->{data}) { |
444 |
$cv->send ($reply->{data}); |
445 |
} elsif ($reply->{error} eq "success") { # success means error... eh.. no... |
446 |
$cv->send; |
447 |
} else { |
448 |
$cv->croak ($reply->{error}); |
449 |
} |
450 |
|
451 |
} else { |
452 |
warn "unexpected reply from mpv, pleasew report: <$1>\n"; |
453 |
} |
454 |
}; |
455 |
warn $@ if $@; |
456 |
} else { |
457 |
$trace->("mpv>" => "$1"); |
458 |
} |
459 |
} |
460 |
} else { |
461 |
$self->stop; |
462 |
$self->on_eof; |
463 |
} |
464 |
}; |
465 |
|
466 |
my $wbuf; |
467 |
my $reqid; |
468 |
|
469 |
$self->{_cmd} = sub { |
470 |
my $cv = AE::cv; |
471 |
|
472 |
$self->{cmdcv}{++$reqid} = $cv; |
473 |
|
474 |
my $cmd = $JSON_ENCODER->encode ({ command => ref $_[0] ? $_[0] : \@_, request_id => $reqid*1 }); |
475 |
|
476 |
# (un-)apply escape_binary hack |
477 |
$cmd =~ s/\xf4\x8e\x97\x9f(..)/sprintf sprintf "\\x%02x", hex $1/ges; # f48e979f == 10e5df in utf-8 |
478 |
|
479 |
$trace->(">mpv" => $cmd); |
480 |
|
481 |
$wbuf .= "$cmd\n"; |
482 |
|
483 |
$self->{ww} ||= AE::io $fh, 1, sub { |
484 |
my $len = syswrite $fh, $wbuf; |
485 |
substr $wbuf, 0, $len, ""; |
486 |
undef $self->{ww} unless length $wbuf; |
487 |
}; |
488 |
|
489 |
$cv |
490 |
}; |
491 |
|
492 |
1 |
493 |
} |
494 |
|
495 |
sub DESTROY { |
496 |
$_[0]->stop; |
497 |
} |
498 |
|
499 |
=item $mpv->stop |
500 |
|
501 |
Ensures that F<mpv> is being stopped, by killing F<mpv> with a C<TERM> |
502 |
signal if needed. After this, you can C<< ->start >> a new instance again. |
503 |
|
504 |
=cut |
505 |
|
506 |
sub stop { |
507 |
my ($self) = @_; |
508 |
|
509 |
delete $self->{rw}; |
510 |
delete $self->{ww}; |
511 |
|
512 |
if ($self->{pid}) { |
513 |
|
514 |
close delete $self->{fh}; # current mpv versions should cleanup on their own on close |
515 |
|
516 |
kill TERM => $self->{pid}; |
517 |
|
518 |
} |
519 |
|
520 |
delete $self->{pid}; |
521 |
delete $self->{cmdcv}; |
522 |
delete $self->{evtid}; |
523 |
delete $self->{evtcb}; |
524 |
delete $self->{obsid}; |
525 |
delete $self->{obscb}; |
526 |
delete $self->{wbuf}; |
527 |
} |
528 |
|
529 |
=item $mpv->on_eof |
530 |
|
531 |
This method is called when F<mpv> quits - usually unexpectedly. The |
532 |
default implementation will call the C<on_eof> code reference specified in |
533 |
the constructor, or do nothing if none was given. |
534 |
|
535 |
For subclassing, see I<SUBCLASSING>, below. |
536 |
|
537 |
=cut |
538 |
|
539 |
sub on_eof { |
540 |
my ($self) = @_; |
541 |
|
542 |
$self->{on_eof}($self) if $self->{on_eof}; |
543 |
} |
544 |
|
545 |
=item $mpv->on_event ($event, $data) |
546 |
|
547 |
This method is called when F<mpv> sends an asynchronous event. The default |
548 |
implementation will call the C<on_event> code reference specified in the |
549 |
constructor, or do nothing if none was given. |
550 |
|
551 |
The first/implicit argument is the C<$mpv> object, the second is the |
552 |
event name (same as C<< $data->{event} >>, purely for convenience), and |
553 |
the third argument is the event object as sent by F<mpv> (sans C<event> |
554 |
key). See L<List of events|https://mpv.io/manual/stable/#list-of-events> |
555 |
in its documentation. |
556 |
|
557 |
For subclassing, see I<SUBCLASSING>, below. |
558 |
|
559 |
=cut |
560 |
|
561 |
sub on_event { |
562 |
my ($self, $key) = @_; |
563 |
|
564 |
$self->{on_event}($self, $key) if $self->{on_event}; |
565 |
} |
566 |
|
567 |
=item $mpv->on_key ($string) |
568 |
|
569 |
Invoked when a key declared by C<< ->bind_key >> is pressed. The default |
570 |
invokes the C<on_key> code reference specified in the constructor with the |
571 |
C<$mpv> object and the key name as arguments, or do nothing if none was |
572 |
given. |
573 |
|
574 |
For more details and examples, see the C<bind_key> method. |
575 |
|
576 |
For subclassing, see I<SUBCLASSING>, below. |
577 |
|
578 |
=cut |
579 |
|
580 |
sub on_key { |
581 |
my ($self, $key) = @_; |
582 |
|
583 |
$self->{on_key}($self, $key) if $self->{on_key}; |
584 |
} |
585 |
|
586 |
=item $mpv->cmd ($command => $arg, $arg...) |
587 |
|
588 |
Queues a command to be sent to F<mpv>, using the given arguments, and |
589 |
immediately return a condvar. |
590 |
|
591 |
See L<the mpv |
592 |
documentation|https://mpv.io/manual/stable/#list-of-input-commands> for |
593 |
details on individual commands. |
594 |
|
595 |
The condvar can be ignored: |
596 |
|
597 |
$mpv->cmd (set_property => "deinterlace", "yes"); |
598 |
|
599 |
Or it can be used to synchronously wait for the command results: |
600 |
|
601 |
$cv = $mpv->cmd (get_property => "video-format"); |
602 |
$format = $cv->recv; |
603 |
|
604 |
# or simpler: |
605 |
|
606 |
$format = $mpv->cmd (get_property => "video-format")->recv; |
607 |
|
608 |
# or even simpler: |
609 |
|
610 |
$format = $mpv->cmd_recv (get_property => "video-format"); |
611 |
|
612 |
Or you can set a callback: |
613 |
|
614 |
$cv = $mpv->cmd (get_property => "video-format"); |
615 |
$cv->cb (sub { |
616 |
my $format = $_[0]->recv; |
617 |
}); |
618 |
|
619 |
On error, the condvar will croak when C<recv> is called. |
620 |
|
621 |
=cut |
622 |
|
623 |
sub cmd { |
624 |
my $self = shift; |
625 |
|
626 |
$self->{_cmd}->(@_) |
627 |
} |
628 |
|
629 |
=item $result = $mpv->cmd_recv ($command => $arg, $arg...) |
630 |
|
631 |
The same as calling C<cmd> and immediately C<recv> on its return |
632 |
value. Useful when you don't want to mess with F<mpv> asynchronously or |
633 |
simply needs to have the result: |
634 |
|
635 |
$mpv->cmd_recv ("stop"); |
636 |
$position = $mpv->cmd_recv ("get_property", "playback-time"); |
637 |
|
638 |
=cut |
639 |
|
640 |
sub cmd_recv { |
641 |
&cmd->recv |
642 |
} |
643 |
|
644 |
=item $mpv->bind_key ($INPUT => $string) |
645 |
|
646 |
This is an extension implement by this module to make it easy to get key |
647 |
events. The way this is implemented is to bind a C<client-message> witha |
648 |
first argument of C<AnyEvent::MPV> and the C<$string> you passed. This |
649 |
C<$string> is then passed to the C<on_key> handle when the key is |
650 |
proessed, e.g.: |
651 |
|
652 |
my $mpv = AnyEvent::MPV->new ( |
653 |
on_key => sub { |
654 |
my ($mpv, $key) = @_; |
655 |
|
656 |
if ($key eq "letmeout") { |
657 |
print "user pressed escape\n"; |
658 |
} |
659 |
}, |
660 |
); |
661 |
|
662 |
$mpv_>bind_key (ESC => "letmeout"); |
663 |
|
664 |
You cna find a list of key names L<in the mpv |
665 |
documentation|https://mpv.io/manual/stable/#key-names>. |
666 |
|
667 |
The key configuration is lost when F<mpv> is stopped and must be (re-)done |
668 |
after every C<start>. |
669 |
|
670 |
=cut |
671 |
|
672 |
sub bind_key { |
673 |
my ($self, $key, $event) = @_; |
674 |
|
675 |
$event =~ s/([^A-Za-z0-9\-_])/sprintf "\\x%02x", ord $1/ge; |
676 |
$self->cmd (keybind => $key => "no-osd script-message AnyEvent::MPV key $event"); |
677 |
} |
678 |
|
679 |
=item [$guard] = $mpv->register_event ($event => $coderef->($mpv, $event, $data)) |
680 |
|
681 |
This method registers a callback to be invoked for a specific |
682 |
event. Whenever the event occurs, it calls the coderef with the C<$mpv> |
683 |
object, the C<$event> name and the event object, just like the C<on_event> |
684 |
method. |
685 |
|
686 |
For a lst of events, see L<the mpv |
687 |
documentation|https://mpv.io/manual/stable/#list-of-events>. Any |
688 |
underscore in the event name is replaced by a minus sign, so you can |
689 |
specify event names using underscores for easier quoting in Perl. |
690 |
|
691 |
In void context, the handler stays registered until C<stop> is called. In |
692 |
any other context, it returns a guard object that, when destroyed, will |
693 |
unregister the handler. |
694 |
|
695 |
You can register multiple handlers for the same event, and this method |
696 |
does not interfere with the C<on_event> mechanism. That is, you can |
697 |
completely ignore this method and handle events in a C<on_event> handler, |
698 |
or mix both approaches as you see fit. |
699 |
|
700 |
=cut |
701 |
|
702 |
sub AnyEvent::MPV::Unevent::DESTROY { |
703 |
my ($evtcb, $event, $evtid) = @{$_[0]}; |
704 |
delete $evtcb->{$event}{$evtid}; |
705 |
} |
706 |
|
707 |
sub register_event { |
708 |
my ($self, $event, $cb) = @_; |
709 |
|
710 |
$event =~ y/_/-/; |
711 |
|
712 |
my $evtid = ++$self->{evtid}; |
713 |
$self->{evtcb}{$event}{$evtid} = $cb; |
714 |
|
715 |
defined wantarray |
716 |
and bless [$self->{evtcb}, $event, $evtid], AnyEvent::MPV::Unevent:: |
717 |
} |
718 |
|
719 |
=item [$guard] = $mpv->observe_property ($name => $coderef->($mpv, $name, $value)) |
720 |
|
721 |
=item [$guard] = $mpv->observe_property_string ($name => $coderef->($mpv, $name, $value)) |
722 |
|
723 |
These methods wrap a registry system around F<mpv>'s C<observe_property> |
724 |
and C<observe_property_string> commands - every time the named property |
725 |
changes, the coderef is invoked with the C<$mpv> object, the name of the |
726 |
property and the new value. |
727 |
|
728 |
For a list of properties that you can observe, see L<the mpv |
729 |
documentation|https://mpv.io/manual/stable/#property-list>. |
730 |
|
731 |
Due to the (sane :) way F<mpv> handles these requests, you will always |
732 |
get a property cxhange event right after registering an observer (meaning |
733 |
you don't have to query the current value), and it is also possible to |
734 |
register multiple observers for the same property - they will all be |
735 |
handled properly. |
736 |
|
737 |
When called in void context, the observer stays in place until F<mpv> |
738 |
is stopped. In any otrher context, these methods return a guard |
739 |
object that, when it goes out of scope, unregisters the observe using |
740 |
C<unobserve_property>. |
741 |
|
742 |
Internally, this method uses observer ids of 2**52 (0x10000000000000) or |
743 |
higher - it will not interfere with lower ovserver ids, so it is possible |
744 |
to completely ignore this system and execute C<observe_property> commands |
745 |
yourself, whilst listening to C<property-change> events - as long as your |
746 |
ids stay below 2**52. |
747 |
|
748 |
Example: register observers for changtes in C<aid> and C<sid>. Note that |
749 |
a dummy statement is added to make sure the method is called in void |
750 |
context. |
751 |
|
752 |
sub register_observers { |
753 |
my ($mpv) = @_; |
754 |
|
755 |
$mpv->observe_property (aid => sub { |
756 |
my ($mpv, $name, $value) = @_; |
757 |
print "property aid (=$name) has changed to $value\n"; |
758 |
}); |
759 |
|
760 |
$mpv->observe_property (sid => sub { |
761 |
my ($mpv, $name, $value) = @_; |
762 |
print "property sid (=$name) has changed to $value\n"; |
763 |
}); |
764 |
|
765 |
() # ensure the above method is called in void context |
766 |
} |
767 |
|
768 |
=cut |
769 |
|
770 |
sub AnyEvent::MPV::Unobserve::DESTROY { |
771 |
my ($mpv, $obscb, $obsid) = @{$_[0]}; |
772 |
|
773 |
delete $obscb->{$obsid}; |
774 |
|
775 |
if ($obscb == $mpv->{obscb}) { |
776 |
$mpv->cmd (unobserve_property => $obsid+0); |
777 |
} |
778 |
} |
779 |
|
780 |
sub _observe_property { |
781 |
my ($self, $type, $property, $cb) = @_; |
782 |
|
783 |
my $obsid = OBSID + ++$self->{obsid}; |
784 |
$self->cmd ($type => $obsid+0, $property); |
785 |
$self->{obscb}{$obsid} = $cb; |
786 |
|
787 |
defined wantarray and do { |
788 |
my $unobserve = bless [$self, $self->{obscb}, $obsid], AnyEvent::MPV::Unobserve::; |
789 |
Scalar::Util::weaken $unobserve->[0]; |
790 |
$unobserve |
791 |
} |
792 |
} |
793 |
|
794 |
sub observe_property { |
795 |
my ($self, $property, $cb) = @_; |
796 |
|
797 |
$self->_observe_property (observe_property => $property, $cb) |
798 |
} |
799 |
|
800 |
sub observe_property_string { |
801 |
my ($self, $property, $cb) = @_; |
802 |
|
803 |
$self->_observe_property (observe_property_string => $property, $cb) |
804 |
} |
805 |
|
806 |
=back |
807 |
|
808 |
=head2 SUBCLASSING |
809 |
|
810 |
Like most perl objects, C<AnyEvent::MPV> objects are implemented as |
811 |
hashes, with the constructor simply storing all passed key-value pairs in |
812 |
the object. If you want to subclass to provide your own C<on_*> methods, |
813 |
be my guest and rummage around in the internals as much as you wish - the |
814 |
only guarantee that this module dcoes is that it will not use keys with |
815 |
double colons in the name, so youc an use those, or chose to simply not |
816 |
care and deal with the breakage. |
817 |
|
818 |
If you don't want to go to the effort of subclassing this module, you can |
819 |
also specify all event handlers as constructor keys. |
820 |
|
821 |
=head1 SEE ALSO |
822 |
|
823 |
L<AnyEvent>, L<the mpv command documentation|https://mpv.io/manual/stable/#command-interface>. |
824 |
|
825 |
=head1 AUTHOR |
826 |
|
827 |
Marc Lehmann <schmorp@schmorp.de> |
828 |
http://home.schmorp.de/ |
829 |
|
830 |
=cut |
831 |
|
832 |
1 |
833 |
|