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