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