ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MPV/MPV.pm
Revision: 1.12
Committed: Mon Mar 20 13:44:40 2023 UTC (13 months, 4 weeks ago) by root
Branch: MAIN
Changes since 1.11: +3 -3 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 C<$mpv>, however, is I<not> decoded to unicode, as data
193 returned by F<mpv> is not generally encoded in unicode, and the encoding
194 is usually unspecified. So if you receive data and expect it to be in
195 unicode, you need to first decode it from UTF-8, but note that this might
196 fail. This is not a limitation of this module - F<mpv> simply does not
197 specify nor guarantee a specific encoding, or any encoding at all, in its
198 protocol.
199
200 =head2 METHODS
201
202 =over
203
204 =cut
205
206 package AnyEvent::MPV;
207
208 use common::sense;
209
210 use Fcntl ();
211 use Scalar::Util ();
212
213 use AnyEvent ();
214 use AnyEvent::Util ();
215
216 our $VERSION = '0.2';
217
218 sub OBSID() { 0x10000000000000 } # 2**52
219
220 our $JSON = eval { require JSON::XS; JSON::XS:: }
221 || do { require JSON::PP; JSON::PP:: };
222
223 our $JSON_CODER =
224
225 our $mpv_path; # last mpv path used
226 our $mpv_optionlist; # output of mpv --list-options
227
228 =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 sub new {
278 my ($class, %kv) = @_;
279
280 bless {
281 mpv => "mpv",
282 args => [],
283 %kv,
284 }, $class
285 }
286
287 =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 # 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 =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 sub start {
356 my ($self, @extra_args) = @_;
357
358 return 0 if $self->{fh};
359
360 # cache optionlist for same "path"
361 ($mpv_path, $mpv_optionlist) = ($self->{mpv}, scalar qx{\Q$self->{mpv}\E --list-options})
362 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 AnyEvent::Util::fh_nonblocking $fh, 1;
370
371 $self->{pid} = fork;
372
373 if ($self->{pid} eq 0) {
374 AnyEvent::Util::fh_nonblocking $slave, 0;
375 fcntl $slave, Fcntl::F_SETFD, 0;
376
377 my $input_file = $options =~ /\s--input-ipc-client\s/ ? "input-ipc-client" : "input-file";
378
379 exec $self->{mpv},
380 qw(--no-input-terminal),
381 ($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 my $trace = delete $self->{trace} || sub { };
391
392 $trace = sub { warn "$_[0] $_[1]\n" } if $trace && !ref $trace;
393
394 my $buf;
395
396 Scalar::Util::weaken $self;
397
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 my $reply = $JSON->new->latin1->decode ($1);
406
407 if (defined (my $event = delete $reply->{event})) {
408 if (
409 $event eq "client-message"
410 and $reply->{args}[0] eq "AnyEvent::MPV"
411 ) {
412 if ($reply->{args}[1] eq "key") {
413 (my $key = $reply->{args}[2]) =~ s/\\x(..)/chr hex $1/ge;
414 $self->on_key ($key);
415 }
416 } elsif (
417 $event eq "property-change"
418 and OBSID <= $reply->{id}
419 ) {
420 if (my $cb = $self->{obscb}{$reply->{id}}) {
421 $cb->($self, $event, $reply->{data});
422 }
423 } else {
424 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 }
434 } elsif (exists $reply->{request_id}) {
435 my $cv = delete $self->{cmdcv}{$reply->{request_id}};
436
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 $self->stop;
461 $self->on_eof;
462 }
463 };
464
465 my $wbuf;
466 my $reqid;
467
468 $self->{_cmd} = sub {
469 my $cv = AE::cv;
470
471 $self->{cmdcv}{++$reqid} = $cv;
472
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 $trace->(">mpv" => $cmd);
479
480 $wbuf .= "$cmd\n";
481
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
488 $cv
489 };
490
491 1
492 }
493
494 sub DESTROY {
495 $_[0]->stop;
496 }
497
498 =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 delete $self->{cmdcv};
521 delete $self->{evtid};
522 delete $self->{evtcb};
523 delete $self->{obsid};
524 delete $self->{obscb};
525 delete $self->{wbuf};
526 }
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 sub on_eof {
539 my ($self) = @_;
540
541 $self->{on_eof}($self) if $self->{on_eof};
542 }
543
544 =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 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
556 For subclassing, see I<SUBCLASSING>, below.
557
558 =cut
559
560 sub on_event {
561 my ($self, $key) = @_;
562
563 $self->{on_event}($self, $key) if $self->{on_event};
564 }
565
566 =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 sub on_key {
580 my ($self, $key) = @_;
581
582 $self->{on_key}($self, $key) if $self->{on_key};
583 }
584
585 =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 sub cmd {
623 my $self = shift;
624
625 $self->{_cmd}->(@_)
626 }
627
628 =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 sub cmd_recv {
640 &cmd->recv
641 }
642
643 =item $mpv->bind_key ($INPUT => $string)
644
645 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
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 You cna find a list of key names L<in the mpv
664 documentation|https://mpv.io/manual/stable/#key-names>.
665
666 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 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 =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, $event, $evtid) = @{$_[0]};
703 delete $evtcb->{$event}{$evtid};
704 }
705
706 sub register_event {
707 my ($self, $event, $cb) = @_;
708
709 $event =~ y/_/-/;
710
711 my $evtid = ++$self->{evtid};
712 $self->{evtcb}{$event}{$evtid} = $cb;
713
714 defined wantarray
715 and bless [$self->{evtcb}, $event, $evtid], AnyEvent::MPV::Unevent::
716 }
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 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 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 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 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 =back
806
807 =head2 SUBCLASSING
808
809 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
817 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
820 =head1 SEE ALSO
821
822 L<AnyEvent>, L<the mpv command documentation|https://mpv.io/manual/stable/#command-interface>.
823
824 =head1 AUTHOR
825
826 Marc Lehmann <schmorp@schmorp.de>
827 http://home.schmorp.de/
828
829 =cut
830
831 1
832