ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MPV/MPV.pm
(Generate patch)

Comparing AnyEvent-MPV/MPV.pm (file contents):
Revision 1.4 by root, Sun Mar 19 20:56:59 2023 UTC vs.
Revision 1.11 by root, Mon Mar 20 13:32:52 2023 UTC

6 6
7 use AnyEvent::MPV; 7 use AnyEvent::MPV;
8 8
9=head1 DESCRIPTION 9=head1 DESCRIPTION
10 10
11This module allows you to remote control F<mpv> (a video player). It also
11This module is an L<AnyEvent> user, you need to make sure that you use and 12is an L<AnyEvent> user, you need to make sure that you use and run a
12run a supported event loop. 13supported event loop.
14
15There are other modules doing this, and I haven't looked much at them
16other than to decide that they don't handle encodings correctly, and since
17none of them use AnyEvent, I wrote my own. When in doubt, have a look at
18them, too.
19
20Knowledge of the L<mpv command
21interface|https://mpv.io/manual/stable/#command-interface> is required to
22use this module.
23
24Features 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
42This module forks an F<mpv> process and uses F<--input-ipc-client> (or
43equivalent) to create a bidirectional communication channel between it and
44the F<mpv> process.
45
46It then speaks the somewhat JSON-looking (but not really being JSON)
47protocol that F<mpv> implements to both send it commands, decode and
48handle replies, and handle asynchronous events.
49
50Here 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
64This starts F<mpv> with the two arguments C<--> and C<$videofile>, which
65it should load and play. It then waits two seconds by starting a timer and
66quits. The C<trace> argument to the constructor makes F<mpv> more verbose
67and also prints the commands and responses, so you can have an idea what
68is going on.
69
70In 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
84This is not usually very useful (you could just run F<mpv> as a simple
85shell 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
104This specifies extra arguments in the constructor - these arguments are
105used 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
107F<--pause> keeps F<mpv> in pause mode (i.e. it does not play the file
108after loading it), and C<--idle=yes> tells F<mpv> to not quit when it does
109not have a playlist - as no files are specified on the command line.
110
111To load a file, we then send it a C<loadfile> command, which accepts, as
112first argument, the URL or path to a video file. To make sure F<mpv> does
113not misinterpret the path as a URL, it was prefixed with F<./> (similarly
114to "protecting" paths in perls C<open>).
115
116Since commands send I<to> F<mpv> are send in UTF-8, we need to escape the
117filename (which might be in any encoding) using the C<esscape_binary>
118method - this is not needed if your filenames are just ascii, or magically
119get interpreted correctly, but if you accept arbitrary filenamews (e.g.
120from the user), you need to do this.
121
122The C<cmd_recv> method then queues the command, waits for a reply and
123returns the reply data (or croaks on error). F<mpv> would, at this point,
124load the file and, if everything was successful, show the first frame and
125pause. Note that, since F<mpv> is implement rather synchronously itself,
126do not expect commands to fail in many circumstances - for example, fit
127he file does not exit, you will likely get an event, but the C<loadfile>
128command itself will run successfully.
129
130To unpause, we send another command, C<set>, to set the C<pause> property
131to C<no>, this time using the C<cmd> method, which queues the command, but
132instead of waiting for a reply, it immediately returns a condvar that cna
133be used to receive results.
134
135This should then cause F<mpv> to start playing the video.
136
137It then again waits two seconds and quits.
138
139Now, just waiting two seconds is rather, eh, unuseful, so let's look at
140receiving 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
171This example uses a global condvar C<$quit> to wait for the file to finish
172playing. Also, most of the logic is now implement in event handlers.
173
174The two events handlers we register are C<start-file>, which is emitted by
175F<mpv> once it has loaded a new file, and C<end-file>, which signals the
176end of a file (underscores are internally replaced by minus signs, so you
177cna speicfy event names with either).
178
179In the C<start-file> event, we again set the C<pause> property to C<no>
180so the movie starts playing. For the C<end-file> event, we tell the main
181program to quit by invoking C<$quit>.
182
183This should conclude the basics of operation. There are a few more
184examples later in the documentation.
185
186=head2 ENCODING CONVENTIONS
187
188As a rule of thumb, all data you pass to this module to be sent to F<mpv>
189is expected to be in unicode. To pass something that isn't, you need to
190escape it using C<escape_binary>.
191
192Data received from C<$mpv>, however, is I<not> decoded to unicode, as data
193returned by F<mpv> is not generally encoded in unicode, and the encoding
194is usually unspecified. So if you receive data and expect it to be in
195unicode, you need to first decode it from UTF-8, but note that this might
196fail. This is not a limitation of this module - F<mpv> simply does not
197specify nor guarantee a specific encoding, or any encoding at all, in its
198protocol.
199
200=head2 METHODS
201
202=over
13 203
14=cut 204=cut
15 205
16package AnyEvent::MPV; 206package AnyEvent::MPV;
17 207
21use Scalar::Util (); 211use Scalar::Util ();
22 212
23use AnyEvent (); 213use AnyEvent ();
24use AnyEvent::Util (); 214use AnyEvent::Util ();
25 215
216our $VERSION = '0.2';
217
218sub OBSID() { 0x10000000000000 } # 2**52
219
26our $JSON = eval { require JSON::XS; JSON::XS:: } 220our $JSON = eval { require JSON::XS; JSON::XS:: }
27 || do { require JSON::PP; JSON::PP:: }; 221 || do { require JSON::PP; JSON::PP:: };
28 222
29our $VERSION = '0.1'; 223our $JSON_CODER =
30 224
31our $mpv_path; # last mpv path used 225our $mpv_path; # last mpv path used
32our $mpv_optionlist; # output of mpv --list-options 226our $mpv_optionlist; # output of mpv --list-options
227
228=item $mpv = AnyEvent::MPV->new (key => value...)
229
230Creates 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
236The path to the F<mpv> binary to use - by default, C<mpv> is used and
237therefore, uses your C<PATH> to find it.
238
239=item args => [...]
240
241Arguments to pass to F<mpv>. These arguments are passed after the
242hardcoded arguments used by this module, but before the arguments passed
243ot C<start>. It does not matter whether you specify your arguments using
244this key, or in the C<start> call, but when you invoke F<mpv> multiple
245times, typically the arguments used for all invocations go here, while
246arguments used for specific invocations (e..g filenames) are passed to
247C<start>.
248
249=item trace => false|true|coderef
250
251Enables tracing if true. In trace mode, output from F<mpv> is printed to
252standard error using a C<< mpv> >> prefix, and commands sent to F<mpv>
253are printed with a C<< >mpv >> prefix.
254
255If a code reference is passed, then instead of printing to standard
256errort, this coderef is invoked with a first arfgument being either
257C<< mpv> >> or C<< >mpv >>, and the second argument being a string to
258display. 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
270These are invoked by the default method implementation of the same name -
271see below.
272
273=back
274
275=cut
33 276
34sub new { 277sub new {
35 my ($class, %kv) = @_; 278 my ($class, %kv) = @_;
36 279
37 bless { 280 bless {
38 mpv => "mpv", 281 mpv => "mpv",
39 args => [], 282 args => [],
40 %kv, 283 %kv,
41 }, $class 284 }, $class
42} 285}
286
287=item $string = $mpv->escape_binary ($string)
288
289This module excects all command data sent to F<mpv> to be in unicode. Some
290things are not, such as filenames. To pass binary data such as filenames
291through a comamnd, you need to escape it using this method.
292
293The simplest example is a C<loadfile> command:
294
295 $mpv->cmd_recv (loadfile => $mpv->escape_binary ($path));
296
297=cut
43 298
44# can be used to escape filenames 299# can be used to escape filenames
45sub escape_binary { 300sub escape_binary {
46 shift; 301 shift;
47 local $_ = shift; 302 local $_ = shift;
48 # we escape every "illegal" octet using U+10e5df HEX. this is later undone in cmd 303 # we escape every "illegal" octet using U+10e5df HEX. this is later undone in cmd
49 s/([\x00-\x1f\x80-\xff])/sprintf "\x{10e5df}%02x", ord $1/ge; 304 s/([\x00-\x1f\x80-\xff])/sprintf "\x{10e5df}%02x", ord $1/ge;
50 $_ 305 $_
51} 306}
52 307
308=item $started = $mpv->start (argument...)
309
310Starts F<mpv>, passing the given arguemnts as extra arguments to
311F<mpv>. If F<mpv> is already running, it returns false, otherwise it
312returns a true value, so you can easily start F<mpv> on demand by calling
313C<start> just before using it, and if it is already running, it will not
314be started again.
315
316The arguments passwd to F<mpv> are a set of hardcoded built-in arguments,
317followed by the arguments specified in the constructor, followed by the
318arguments passwd to this method. The built-in arguments currently are
319F<--no-input-terminal>, F<--really-quiet> (or F<--quiet> in C<trace>
320mode), and C<--input-ipc-client> (or equivalent).
321
322Some 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
327don'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
330inspect/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
342The 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
350You can immediately starting sending commands when this method returns,
351even if F<mpv> has not yet started.
352
353=cut
354
53sub start { 355sub start {
54 my ($self, @extra_args) = @_; 356 my ($self, @extra_args) = @_;
55 357
56 return 0 if $self->{fh}; 358 return 0 if $self->{fh};
57 359
73 fcntl $slave, Fcntl::F_SETFD, 0; 375 fcntl $slave, Fcntl::F_SETFD, 0;
74 376
75 my $input_file = $options =~ /\s--input-ipc-client\s/ ? "input-ipc-client" : "input-file"; 377 my $input_file = $options =~ /\s--input-ipc-client\s/ ? "input-ipc-client" : "input-file";
76 378
77 exec $self->{mpv}, 379 exec $self->{mpv},
78 qw(--no-input-terminal --idle=yes --pause), 380 qw(--no-input-terminal),
79 ($self->{trace} ? "--quiet" : "--really-quiet"), 381 ($self->{trace} ? "--quiet" : "--really-quiet"),
80 "--$input_file=fd://" . (fileno $slave), 382 "--$input_file=fd://" . (fileno $slave),
81 @{ $self->{args} }, 383 @{ $self->{args} },
82 @extra_args; 384 @extra_args;
83 exit 1; 385 exit 1;
88 my $trace = delete $self->{trace} || sub { }; 390 my $trace = delete $self->{trace} || sub { };
89 391
90 $trace = sub { warn "$_[0] $_[1]\n" } if $trace && !ref $trace; 392 $trace = sub { warn "$_[0] $_[1]\n" } if $trace && !ref $trace;
91 393
92 my $buf; 394 my $buf;
93 my $wbuf;
94 395
95 Scalar::Util::weaken $self; 396 Scalar::Util::weaken $self;
96 397
97 $self->{rw} = AE::io $fh, 0, sub { 398 $self->{rw} = AE::io $fh, 0, sub {
98 if (sysread $fh, $buf, 8192, length $buf) { 399 if (sysread $fh, $buf, 8192, length $buf) {
99 while ($buf =~ s/^([^\n]+)\n//) { 400 while ($buf =~ s/^([^\n]+)\n//) {
100 $trace->("mpv>" => "$1"); 401 $trace->("mpv>" => "$1");
101 402
102 if ("{" eq substr $1, 0, 1) { 403 if ("{" eq substr $1, 0, 1) {
103 eval { 404 eval {
104 my $reply = JSON::XS->new->latin1->decode ($1); 405 my $reply = $JSON->new->latin1->decode ($1);
105 406
106 if (exists $reply->{event}) { 407 if (defined (my $event = delete $reply->{event})) {
107 if ( 408 if (
108 $reply->{event} eq "client-message" 409 $event eq "client-message"
109 and $reply->{args}[0] eq "AnyEvent::MPV" 410 and $reply->{args}[0] eq "AnyEvent::MPV"
110 ) { 411 ) {
111 if ($reply->{args}[1] eq "key") { 412 if ($reply->{args}[1] eq "key") {
112 (my $key = $reply->{args}[2]) =~ s/\\x(..)/chr hex $1/ge; 413 (my $key = $reply->{args}[2]) =~ s/\\x(..)/chr hex $1/ge;
113 $self->on_key ($key); 414 $self->on_key ($key);
114 } 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 }
115 } else { 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
116 $self->on_event ($reply); 432 $self->on_event ($event, $reply);
117 } 433 }
118 } elsif (exists $reply->{request_id}) { 434 } elsif (exists $reply->{request_id}) {
119 my $cv = delete $self->{cmd_cv}{$reply->{request_id}}; 435 my $cv = delete $self->{cmdcv}{$reply->{request_id}};
120 436
121 unless ($cv) { 437 unless ($cv) {
122 warn "no cv found for request id <$reply->{request_id}>\n"; 438 warn "no cv found for request id <$reply->{request_id}>\n";
123 next; 439 next;
124 } 440 }
144 $self->stop; 460 $self->stop;
145 $self->on_eof; 461 $self->on_eof;
146 } 462 }
147 }; 463 };
148 464
465 my $wbuf;
466 my $reqid;
467
149 $self->{_send} = sub { 468 $self->{_cmd} = sub {
150 $wbuf .= "$_[0]\n"; 469 my $cv = AE::cv;
151 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
152 $trace->(">mpv" => "$_[0]"); 478 $trace->(">mpv" => $cmd);
479
480 $wbuf .= "$cmd\n";
153 481
154 $self->{ww} ||= AE::io $fh, 1, sub { 482 $self->{ww} ||= AE::io $fh, 1, sub {
155 my $len = syswrite $fh, $wbuf; 483 my $len = syswrite $fh, $wbuf;
156 substr $wbuf, 0, $len, ""; 484 substr $wbuf, 0, $len, "";
157 undef $self->{ww} unless length $wbuf; 485 undef $self->{ww} unless length $wbuf;
158 }; 486 };
487
488 $cv
159 }; 489 };
160 490
161 1 491 1
162} 492}
493
494sub DESTROY {
495 $_[0]->stop;
496}
497
498=item $mpv->stop
499
500Ensures that F<mpv> is being stopped, by killing F<mpv> with a C<TERM>
501signal if needed. After this, you can C<< ->start >> a new instance again.
502
503=cut
504
505sub 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
530This method is called when F<mpv> quits - usually unexpectedly. The
531default implementation will call the C<on_eof> code reference specified in
532the constructor, or do nothing if none was given.
533
534For subclassing, see I<SUBCLASSING>, below.
535
536=cut
163 537
164sub on_eof { 538sub on_eof {
165 my ($self) = @_; 539 my ($self) = @_;
166 540
167 $self->{on_eof}($self) if $self->{on_eof}; 541 $self->{on_eof}($self) if $self->{on_eof};
168} 542}
169 543
544=item $mpv->on_event ($event, $data)
545
546This method is called when F<mpv> sends an asynchronous event. The default
547implementation will call the C<on_event> code reference specified in the
548constructor, or do nothing if none was given.
549
550The first/implicit argument is the C<$mpv> object, the second is the
551event name (same as C<< $data->{event} >>, purely for convenience), and
552the third argument is the event object as sent by F<mpv> (sans C<event>
553key). See L<List of events|https://mpv.io/manual/stable/#list-of-events>
554in its documentation.
555
556For subclassing, see I<SUBCLASSING>, below.
557
558=cut
559
170sub on_event { 560sub on_event {
171 my ($self, $key) = @_; 561 my ($self, $key) = @_;
172 562
173 $self->{on_event}($self, $key) if $self->{on_event}; 563 $self->{on_event}($self, $key) if $self->{on_event};
174} 564}
175 565
566=item $mpv->on_key ($string)
567
568Invoked when a key declared by C<< ->bind_key >> is pressed. The default
569invokes the C<on_key> code reference specified in the constructor with the
570C<$mpv> object and the key name as arguments, or do nothing if none was
571given.
572
573For more details and examples, see the C<bind_key> method.
574
575For subclassing, see I<SUBCLASSING>, below.
576
577=cut
578
176sub on_key { 579sub on_key {
177 my ($self, $key) = @_; 580 my ($self, $key) = @_;
178 581
179 $self->{on_key}($self, $key) if $self->{on_key}; 582 $self->{on_key}($self, $key) if $self->{on_key};
180} 583}
181 584
585=item $mpv->cmd ($command => $arg, $arg...)
586
587Queues a command to be sent to F<mpv>, using the given arguments, and
588immediately return a condvar.
589
590See L<the mpv
591documentation|https://mpv.io/manual/stable/#list-of-input-commands> for
592details on individual commands.
593
594The condvar can be ignored:
595
596 $mpv->cmd (set_property => "deinterlace", "yes");
597
598Or 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
611Or 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
618On error, the condvar will croak when C<recv> is called.
619
620=cut
621
182sub cmd { 622sub cmd {
183 my ($self, @cmd) = @_; 623 my $self = shift;
184 624
185 my $cv = AE::cv; 625 $self->{_cmd}->(@_)
186
187 my $reqid = ++$self->{reqid};
188 $self->{cmd_cv}{$reqid} = $cv;
189
190 my $cmd = JSON::XS::encode_json { command => ref $cmd[0] ? $cmd[0] : \@cmd, request_id => $reqid*1 };
191
192 # (un-)apply escape_binary hack
193 $cmd =~ s/\xf4\x8e\x97\x9f(..)/sprintf sprintf "\\x%02x", hex $1/ges; # f48e979f == 10e5df in utf-8
194
195 $self->{_send}($cmd);
196
197 $cv
198} 626}
627
628=item $result = $mpv->cmd_recv ($command => $arg, $arg...)
629
630The same as calling C<cmd> and immediately C<recv> on its return
631value. Useful when you don't want to mess with F<mpv> asynchronously or
632simply needs to have the result:
633
634 $mpv->cmd_recv ("stop");
635 $position = $mpv->cmd_recv ("get_property", "playback-time");
636
637=cut
199 638
200sub cmd_recv { 639sub cmd_recv {
201 &cmd->recv 640 &cmd->recv
202} 641}
203 642
643=item $mpv->bind_key ($INPUT => $string)
644
645This is an extension implement by this module to make it easy to get key
646events. The way this is implemented is to bind a C<client-message> witha
647first argument of C<AnyEvent::MPV> and the C<$string> you passed. This
648C<$string> is then passed to the C<on_key> handle when the key is
649proessed, 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
663You cna find a list of key names L<in the mpv
664documentation|https://mpv.io/manual/stable/#key-names>.
665
666The key configuration is lost when F<mpv> is stopped and must be (re-)done
667after every C<start>.
668
669=cut
670
204sub bind_key { 671sub bind_key {
205 my ($self, $key, $event) = @_; 672 my ($self, $key, $event) = @_;
206 673
207 $event =~ s/([^A-Za-z0-9\-_])/sprintf "\\x%02x", ord $1/ge; 674 $event =~ s/([^A-Za-z0-9\-_])/sprintf "\\x%02x", ord $1/ge;
208 $self->cmd (keybind => $key => "no-osd script-message AnyEvent::MPV key $event"); 675 $self->cmd (keybind => $key => "no-osd script-message AnyEvent::MPV key $event");
209} 676}
210 677
211sub stop { 678=item [$guard] = $mpv->register_event ($event => $coderef->($mpv, $event, $data))
679
680This method registers a callback to be invoked for a specific
681event. Whenever the event occurs, it calls the coderef with the C<$mpv>
682object, the C<$event> name and the event object, just like the C<on_event>
683method.
684
685For a lst of events, see L<the mpv
686documentation|https://mpv.io/manual/stable/#list-of-events>. Any
687underscore in the event name is replaced by a minus sign, so you can
688specify event names using underscores for easier quoting in Perl.
689
690In void context, the handler stays registered until C<stop> is called. In
691any other context, it returns a guard object that, when destroyed, will
692unregister the handler.
693
694You can register multiple handlers for the same event, and this method
695does not interfere with the C<on_event> mechanism. That is, you can
696completely ignore this method and handle events in a C<on_event> handler,
697or mix both approaches as you see fit.
698
699=cut
700
701sub AnyEvent::MPV::Unevent::DESTROY {
702 my ($evtcb, $evtid) = @{$_[0]};
703 delete $evtcb->{$evtid};
704}
705
706sub 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}, $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
722These methods wrap a registry system around F<mpv>'s C<observe_property>
723and C<observe_property_string> commands - every time the named property
724changes, the coderef is invoked with the C<$mpv> object, the name of the
725property and the new value.
726
727For a list of properties that you can observe, see L<the mpv
728documentation|https://mpv.io/manual/stable/#property-list>.
729
730Due to the (sane :) way F<mpv> handles these requests, you will always
731get a property cxhange event right after registering an observer (meaning
732you don't have to query the current value), and it is also possible to
733register multiple observers for the same property - they will all be
734handled properly.
735
736When called in void context, the observer stays in place until F<mpv>
737is stopped. In any otrher context, these methods return a guard
738object that, when it goes out of scope, unregisters the observe using
739C<unobserve_property>.
740
741Internally, this method uses observer ids of 2**52 (0x10000000000000) or
742higher - it will not interfere with lower ovserver ids, so it is possible
743to completely ignore this system and execute C<observe_property> commands
744yourself, whilst listening to C<property-change> events - as long as your
745ids stay below 2**52.
746
747Example: register observers for changtes in C<aid> and C<sid>. Note that
748a dummy statement is added to make sure the method is called in void
749context.
750
751 sub register_observers {
212 my ($self) = @_; 752 my ($mpv) = @_;
213 753
214 if ($self->{pid}) { 754 $mpv->observe_property (aid => sub {
215 delete $self->{rw}; 755 my ($mpv, $name, $value) = @_;
216 delete $self->{ww}; 756 print "property aid (=$name) has changed to $value\n";
757 });
217 758
218 close delete $self->{fh}; # current mpv versions should cleanup on their own on close 759 $mpv->observe_property (sid => sub {
760 my ($mpv, $name, $value) = @_;
761 print "property sid (=$name) has changed to $value\n";
762 });
219 763
220 kill TERM => $self->{pid}; 764 () # ensure the above method is called in void context
221
222 delete $self->{pid};
223 } 765 }
766
767=cut
768
769sub 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 }
224} 777}
778
779sub _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
793sub observe_property {
794 my ($self, $property, $cb) = @_;
795
796 $self->_observe_property (observe_property => $property, $cb)
797}
798
799sub 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
809Like most perl objects, C<AnyEvent::MPV> objects are implemented as
810hashes, with the constructor simply storing all passed key-value pairs in
811the object. If you want to subclass to provide your own C<on_*> methods,
812be my guest and rummage around in the internals as much as you wish - the
813only guarantee that this module dcoes is that it will not use keys with
814double colons in the name, so youc an use those, or chose to simply not
815care and deal with the breakage.
816
817If you don't want to go to the effort of subclassing this module, you can
818also specify all event handlers as constructor keys.
225 819
226=head1 SEE ALSO 820=head1 SEE ALSO
227 821
228L<AnyEvent>. 822L<AnyEvent>, L<the mpv command documentation|https://mpv.io/manual/stable/#command-interface>.
229 823
230=head1 AUTHOR 824=head1 AUTHOR
231 825
232 Marc Lehmann <schmorp@schmorp.de> 826 Marc Lehmann <schmorp@schmorp.de>
233 http://home.schmorp.de/ 827 http://home.schmorp.de/

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines