ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MPV/MPV.pm
Revision: 1.15
Committed: Wed Mar 22 01:00:36 2023 UTC (13 months, 4 weeks ago) by root
Branch: MAIN
Changes since 1.14: +1 -1 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
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     );
153    
154     $mpv->start;
155 root 1.11
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 root 1.5 $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 root 1.11 playing. Also, most of the logic is now implement in event handlers.
173 root 1.5
174 root 1.11 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 root 1.5
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 root 1.15 Data received from F<mpv>, however, is I<not> decoded to unicode, as data
193 root 1.5 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 root 1.1
204     =cut
205    
206     package AnyEvent::MPV;
207    
208     use common::sense;
209    
210 root 1.2 use Fcntl ();
211     use Scalar::Util ();
212    
213 root 1.1 use AnyEvent ();
214     use AnyEvent::Util ();
215    
216 root 1.10 our $VERSION = '0.2';
217 root 1.9
218     sub OBSID() { 0x10000000000000 } # 2**52
219    
220 root 1.1 our $JSON = eval { require JSON::XS; JSON::XS:: }
221     || do { require JSON::PP; JSON::PP:: };
222    
223 root 1.13 our $JSON_ENCODER = $JSON->new->utf8;
224     our $JSON_DECODER = $JSON->new->latin1;
225 root 1.5
226 root 1.1 our $mpv_path; # last mpv path used
227     our $mpv_optionlist; # output of mpv --list-options
228    
229 root 1.5 =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 root 1.1 sub new {
279     my ($class, %kv) = @_;
280    
281     bless {
282     mpv => "mpv",
283     args => [],
284     %kv,
285     }, $class
286     }
287    
288 root 1.5 =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 root 1.1 # 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 root 1.5 =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 root 1.1 sub start {
357     my ($self, @extra_args) = @_;
358    
359 root 1.4 return 0 if $self->{fh};
360 root 1.1
361     # cache optionlist for same "path"
362 root 1.2 ($mpv_path, $mpv_optionlist) = ($self->{mpv}, scalar qx{\Q$self->{mpv}\E --list-options})
363 root 1.1 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 root 1.2 AnyEvent::Util::fh_nonblocking $fh, 1;
371 root 1.1
372 root 1.2 $self->{pid} = fork;
373 root 1.1
374     if ($self->{pid} eq 0) {
375 root 1.2 AnyEvent::Util::fh_nonblocking $slave, 0;
376     fcntl $slave, Fcntl::F_SETFD, 0;
377    
378 root 1.1 my $input_file = $options =~ /\s--input-ipc-client\s/ ? "input-ipc-client" : "input-file";
379    
380     exec $self->{mpv},
381 root 1.5 qw(--no-input-terminal),
382 root 1.1 ($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 root 1.2 my $trace = delete $self->{trace} || sub { };
392 root 1.1
393     $trace = sub { warn "$_[0] $_[1]\n" } if $trace && !ref $trace;
394    
395     my $buf;
396 root 1.2
397     Scalar::Util::weaken $self;
398 root 1.1
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 root 1.13 my $reply = $JSON_DECODER->decode ($1);
407 root 1.1
408 root 1.11 if (defined (my $event = delete $reply->{event})) {
409 root 1.1 if (
410 root 1.11 $event eq "client-message"
411 root 1.1 and $reply->{args}[0] eq "AnyEvent::MPV"
412     ) {
413 root 1.3 if ($reply->{args}[1] eq "key") {
414 root 1.4 (my $key = $reply->{args}[2]) =~ s/\\x(..)/chr hex $1/ge;
415     $self->on_key ($key);
416 root 1.3 }
417 root 1.9 } elsif (
418 root 1.11 $event eq "property-change"
419 root 1.9 and OBSID <= $reply->{id}
420     ) {
421     if (my $cb = $self->{obscb}{$reply->{id}}) {
422 root 1.11 $cb->($self, $event, $reply->{data});
423 root 1.9 }
424 root 1.1 } else {
425 root 1.11 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 root 1.1 }
435     } elsif (exists $reply->{request_id}) {
436 root 1.9 my $cv = delete $self->{cmdcv}{$reply->{request_id}};
437 root 1.1
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 root 1.2 $self->stop;
462 root 1.1 $self->on_eof;
463     }
464     };
465    
466 root 1.8 my $wbuf;
467     my $reqid;
468    
469     $self->{_cmd} = sub {
470     my $cv = AE::cv;
471    
472 root 1.9 $self->{cmdcv}{++$reqid} = $cv;
473 root 1.8
474 root 1.13 my $cmd = $JSON_ENCODER->encode ({ command => ref $_[0] ? $_[0] : \@_, request_id => $reqid*1 });
475 root 1.8
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 root 1.9 $trace->(">mpv" => $cmd);
480    
481 root 1.8 $wbuf .= "$cmd\n";
482 root 1.1
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 root 1.8
489     $cv
490 root 1.1 };
491 root 1.4
492     1
493 root 1.1 }
494    
495 root 1.8 sub DESTROY {
496     $_[0]->stop;
497     }
498    
499 root 1.5 =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 root 1.9 delete $self->{cmdcv};
522 root 1.11 delete $self->{evtid};
523     delete $self->{evtcb};
524 root 1.8 delete $self->{obsid};
525 root 1.9 delete $self->{obscb};
526 root 1.8 delete $self->{wbuf};
527 root 1.5 }
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 root 1.1 sub on_eof {
540     my ($self) = @_;
541    
542     $self->{on_eof}($self) if $self->{on_eof};
543     }
544    
545 root 1.5 =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 root 1.7 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 root 1.5
557     For subclassing, see I<SUBCLASSING>, below.
558    
559     =cut
560    
561 root 1.1 sub on_event {
562     my ($self, $key) = @_;
563    
564     $self->{on_event}($self, $key) if $self->{on_event};
565     }
566    
567 root 1.5 =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 root 1.2 sub on_key {
581 root 1.1 my ($self, $key) = @_;
582    
583 root 1.2 $self->{on_key}($self, $key) if $self->{on_key};
584 root 1.1 }
585    
586 root 1.5 =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 root 1.1 sub cmd {
624 root 1.8 my $self = shift;
625 root 1.1
626 root 1.8 $self->{_cmd}->(@_)
627 root 1.1 }
628    
629 root 1.5 =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 root 1.4 sub cmd_recv {
641     &cmd->recv
642     }
643    
644 root 1.5 =item $mpv->bind_key ($INPUT => $string)
645    
646 root 1.11 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 root 1.5
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 root 1.11 You cna find a list of key names L<in the mpv
665     documentation|https://mpv.io/manual/stable/#key-names>.
666    
667 root 1.5 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 root 1.4 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 root 1.11 =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 root 1.14 Note that unlike commands, event handlers are registered immediately, that
701     is, you can issue a command, then register an event handler and then get
702     an event for this handler I<before> the command is even sent to F<mpv>. If
703     this kind of race is an issue, you can issue a dummy command such as
704     C<get_version> and register the handler when the reply is received.
705    
706 root 1.11 =cut
707    
708     sub AnyEvent::MPV::Unevent::DESTROY {
709 root 1.12 my ($evtcb, $event, $evtid) = @{$_[0]};
710     delete $evtcb->{$event}{$evtid};
711 root 1.11 }
712    
713     sub register_event {
714     my ($self, $event, $cb) = @_;
715    
716     $event =~ y/_/-/;
717 root 1.9
718 root 1.11 my $evtid = ++$self->{evtid};
719     $self->{evtcb}{$event}{$evtid} = $cb;
720 root 1.9
721 root 1.11 defined wantarray
722 root 1.12 and bless [$self->{evtcb}, $event, $evtid], AnyEvent::MPV::Unevent::
723 root 1.9 }
724    
725     =item [$guard] = $mpv->observe_property ($name => $coderef->($mpv, $name, $value))
726    
727     =item [$guard] = $mpv->observe_property_string ($name => $coderef->($mpv, $name, $value))
728    
729     These methods wrap a registry system around F<mpv>'s C<observe_property>
730     and C<observe_property_string> commands - every time the named property
731     changes, the coderef is invoked with the C<$mpv> object, the name of the
732     property and the new value.
733    
734     For a list of properties that you can observe, see L<the mpv
735     documentation|https://mpv.io/manual/stable/#property-list>.
736    
737     Due to the (sane :) way F<mpv> handles these requests, you will always
738     get a property cxhange event right after registering an observer (meaning
739     you don't have to query the current value), and it is also possible to
740     register multiple observers for the same property - they will all be
741     handled properly.
742    
743     When called in void context, the observer stays in place until F<mpv>
744     is stopped. In any otrher context, these methods return a guard
745     object that, when it goes out of scope, unregisters the observe using
746     C<unobserve_property>.
747    
748 root 1.10 Internally, this method uses observer ids of 2**52 (0x10000000000000) or
749     higher - it will not interfere with lower ovserver ids, so it is possible
750     to completely ignore this system and execute C<observe_property> commands
751     yourself, whilst listening to C<property-change> events - as long as your
752     ids stay below 2**52.
753    
754 root 1.9 Example: register observers for changtes in C<aid> and C<sid>. Note that
755     a dummy statement is added to make sure the method is called in void
756     context.
757    
758     sub register_observers {
759     my ($mpv) = @_;
760    
761     $mpv->observe_property (aid => sub {
762     my ($mpv, $name, $value) = @_;
763     print "property aid (=$name) has changed to $value\n";
764     });
765    
766     $mpv->observe_property (sid => sub {
767     my ($mpv, $name, $value) = @_;
768     print "property sid (=$name) has changed to $value\n";
769     });
770    
771     () # ensure the above method is called in void context
772     }
773    
774     =cut
775    
776 root 1.11 sub AnyEvent::MPV::Unobserve::DESTROY {
777     my ($mpv, $obscb, $obsid) = @{$_[0]};
778    
779     delete $obscb->{$obsid};
780    
781     if ($obscb == $mpv->{obscb}) {
782     $mpv->cmd (unobserve_property => $obsid+0);
783     }
784     }
785    
786 root 1.9 sub _observe_property {
787     my ($self, $type, $property, $cb) = @_;
788    
789     my $obsid = OBSID + ++$self->{obsid};
790     $self->cmd ($type => $obsid+0, $property);
791     $self->{obscb}{$obsid} = $cb;
792    
793     defined wantarray and do {
794     my $unobserve = bless [$self, $self->{obscb}, $obsid], AnyEvent::MPV::Unobserve::;
795     Scalar::Util::weaken $unobserve->[0];
796     $unobserve
797     }
798     }
799    
800     sub observe_property {
801     my ($self, $property, $cb) = @_;
802    
803     $self->_observe_property (observe_property => $property, $cb)
804     }
805    
806     sub observe_property_string {
807     my ($self, $property, $cb) = @_;
808    
809     $self->_observe_property (observe_property_string => $property, $cb)
810     }
811    
812 root 1.5 =back
813 root 1.1
814 root 1.5 =head2 SUBCLASSING
815 root 1.1
816 root 1.5 Like most perl objects, C<AnyEvent::MPV> objects are implemented as
817     hashes, with the constructor simply storing all passed key-value pairs in
818     the object. If you want to subclass to provide your own C<on_*> methods,
819     be my guest and rummage around in the internals as much as you wish - the
820     only guarantee that this module dcoes is that it will not use keys with
821     double colons in the name, so youc an use those, or chose to simply not
822     care and deal with the breakage.
823 root 1.1
824 root 1.5 If you don't want to go to the effort of subclassing this module, you can
825     also specify all event handlers as constructor keys.
826 root 1.1
827     =head1 SEE ALSO
828    
829 root 1.5 L<AnyEvent>, L<the mpv command documentation|https://mpv.io/manual/stable/#command-interface>.
830 root 1.1
831     =head1 AUTHOR
832    
833     Marc Lehmann <schmorp@schmorp.de>
834     http://home.schmorp.de/
835    
836     =cut
837    
838     1
839