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

# Content
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 F<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 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 =cut
707
708 sub AnyEvent::MPV::Unevent::DESTROY {
709 my ($evtcb, $event, $evtid) = @{$_[0]};
710 delete $evtcb->{$event}{$evtid};
711 }
712
713 sub register_event {
714 my ($self, $event, $cb) = @_;
715
716 $event =~ y/_/-/;
717
718 my $evtid = ++$self->{evtid};
719 $self->{evtcb}{$event}{$evtid} = $cb;
720
721 defined wantarray
722 and bless [$self->{evtcb}, $event, $evtid], AnyEvent::MPV::Unevent::
723 }
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 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 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 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 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 =back
813
814 =head2 SUBCLASSING
815
816 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
824 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
827 =head1 SEE ALSO
828
829 L<AnyEvent>, L<the mpv command documentation|https://mpv.io/manual/stable/#command-interface>.
830
831 =head1 AUTHOR
832
833 Marc Lehmann <schmorp@schmorp.de>
834 http://home.schmorp.de/
835
836 =cut
837
838 1
839