ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MPV/MPV.pm
Revision: 1.9
Committed: Mon Mar 20 12:23:21 2023 UTC (13 months, 4 weeks ago) by root
Branch: MAIN
Changes since 1.8: +101 -8 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 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
202 =cut
203
204 package AnyEvent::MPV;
205
206 use common::sense;
207
208 use Fcntl ();
209 use Scalar::Util ();
210
211 use AnyEvent ();
212 use AnyEvent::Util ();
213
214 our $VERSION = '0.1';
215
216 sub OBSID() { 0x10000000000000 } # 2**52
217
218 our $JSON = eval { require JSON::XS; JSON::XS:: }
219 || do { require JSON::PP; JSON::PP:: };
220
221 our $JSON_CODER =
222
223 our $mpv_path; # last mpv path used
224 our $mpv_optionlist; # output of mpv --list-options
225
226 =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 sub new {
276 my ($class, %kv) = @_;
277
278 bless {
279 mpv => "mpv",
280 args => [],
281 %kv,
282 }, $class
283 }
284
285 =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 # 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 =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 sub start {
354 my ($self, @extra_args) = @_;
355
356 return 0 if $self->{fh};
357
358 $self->{obscb} = {};
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 (exists $reply->{event}) {
408 if (
409 $reply->{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 $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 } else {
424 $self->on_event (delete $reply->{event}, $reply);
425 }
426 } elsif (exists $reply->{request_id}) {
427 my $cv = delete $self->{cmdcv}{$reply->{request_id}};
428
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 $self->stop;
453 $self->on_eof;
454 }
455 };
456
457 my $wbuf;
458 my $reqid;
459
460 $self->{_cmd} = sub {
461 my $cv = AE::cv;
462
463 $self->{cmdcv}{++$reqid} = $cv;
464
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 $trace->(">mpv" => $cmd);
471
472 $wbuf .= "$cmd\n";
473
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
480 $cv
481 };
482
483 1
484 }
485
486 sub DESTROY {
487 $_[0]->stop;
488 }
489
490 =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 delete $self->{cmdcv};
513 delete $self->{obsid};
514 delete $self->{obscb};
515 delete $self->{wbuf};
516 }
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 sub on_eof {
529 my ($self) = @_;
530
531 $self->{on_eof}($self) if $self->{on_eof};
532 }
533
534 =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 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
546 For subclassing, see I<SUBCLASSING>, below.
547
548 =cut
549
550 sub on_event {
551 my ($self, $key) = @_;
552
553 $self->{on_event}($self, $key) if $self->{on_event};
554 }
555
556 =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 sub on_key {
570 my ($self, $key) = @_;
571
572 $self->{on_key}($self, $key) if $self->{on_key};
573 }
574
575 =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 sub cmd {
613 my $self = shift;
614
615 $self->{_cmd}->(@_)
616 }
617
618 =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 sub cmd_recv {
630 &cmd->recv
631 }
632
633 =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 passed to the C<on_key> handle when the key is proessed, e.g.:
638
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 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 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 Example: register observers for changtes in C<aid> and C<sid>. Note that
697 a dummy statement is added to make sure the method is called in void
698 context.
699
700 sub register_observers {
701 my ($mpv) = @_;
702
703 $mpv->observe_property (aid => sub {
704 my ($mpv, $name, $value) = @_;
705 print "property aid (=$name) has changed to $value\n";
706 });
707
708 $mpv->observe_property (sid => sub {
709 my ($mpv, $name, $value) = @_;
710 print "property sid (=$name) has changed to $value\n";
711 });
712
713 () # ensure the above method is called in void context
714 }
715
716 =cut
717
718 sub _observe_property {
719 my ($self, $type, $property, $cb) = @_;
720
721 my $obsid = OBSID + ++$self->{obsid};
722 $self->cmd ($type => $obsid+0, $property);
723 $self->{obscb}{$obsid} = $cb;
724
725 defined wantarray and do {
726 my $unobserve = bless [$self, $self->{obscb}, $obsid], AnyEvent::MPV::Unobserve::;
727 Scalar::Util::weaken $unobserve->[0];
728 $unobserve
729 }
730 }
731
732 sub observe_property {
733 my ($self, $property, $cb) = @_;
734
735 $self->_observe_property (observe_property => $property, $cb)
736 }
737
738 sub observe_property_string {
739 my ($self, $property, $cb) = @_;
740
741 $self->_observe_property (observe_property_string => $property, $cb)
742 }
743
744 =back
745
746 =head2 SUBCLASSING
747
748 Like most perl objects, C<AnyEvent::MPV> objects are implemented as
749 hashes, with the constructor simply storing all passed key-value pairs in
750 the object. If you want to subclass to provide your own C<on_*> methods,
751 be my guest and rummage around in the internals as much as you wish - the
752 only guarantee that this module dcoes is that it will not use keys with
753 double colons in the name, so youc an use those, or chose to simply not
754 care and deal with the breakage.
755
756 If you don't want to go to the effort of subclassing this module, you can
757 also specify all event handlers as constructor keys.
758
759 =head1 SEE ALSO
760
761 L<AnyEvent>, L<the mpv command documentation|https://mpv.io/manual/stable/#command-interface>.
762
763 =head1 AUTHOR
764
765 Marc Lehmann <schmorp@schmorp.de>
766 http://home.schmorp.de/
767
768 =cut
769
770 1
771