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.1 by root, Sun Mar 19 19:36:40 2023 UTC vs.
Revision 1.13 by root, Mon Mar 20 14:42:40 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
18use common::sense; 208use common::sense;
209
210use Fcntl ();
211use Scalar::Util ();
19 212
20use AnyEvent (); 213use AnyEvent ();
21use AnyEvent::Util (); 214use AnyEvent::Util ();
22 215
216our $VERSION = '0.2';
217
218sub OBSID() { 0x10000000000000 } # 2**52
219
23our $JSON = eval { require JSON::XS; JSON::XS:: } 220our $JSON = eval { require JSON::XS; JSON::XS:: }
24 || do { require JSON::PP; JSON::PP:: }; 221 || do { require JSON::PP; JSON::PP:: };
25 222
26our $VERSION = '0.1'; 223our $JSON_ENCODER = $JSON->new->utf8;
224our $JSON_DECODER = $JSON->new->latin1;
27 225
28our $mpv_path; # last mpv path used 226our $mpv_path; # last mpv path used
29our $mpv_optionlist; # output of mpv --list-options 227our $mpv_optionlist; # output of mpv --list-options
228
229=item $mpv = AnyEvent::MPV->new (key => value...)
230
231Creates 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
237The path to the F<mpv> binary to use - by default, C<mpv> is used and
238therefore, uses your C<PATH> to find it.
239
240=item args => [...]
241
242Arguments to pass to F<mpv>. These arguments are passed after the
243hardcoded arguments used by this module, but before the arguments passed
244ot C<start>. It does not matter whether you specify your arguments using
245this key, or in the C<start> call, but when you invoke F<mpv> multiple
246times, typically the arguments used for all invocations go here, while
247arguments used for specific invocations (e..g filenames) are passed to
248C<start>.
249
250=item trace => false|true|coderef
251
252Enables tracing if true. In trace mode, output from F<mpv> is printed to
253standard error using a C<< mpv> >> prefix, and commands sent to F<mpv>
254are printed with a C<< >mpv >> prefix.
255
256If a code reference is passed, then instead of printing to standard
257errort, this coderef is invoked with a first arfgument being either
258C<< mpv> >> or C<< >mpv >>, and the second argument being a string to
259display. 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
271These are invoked by the default method implementation of the same name -
272see below.
273
274=back
275
276=cut
30 277
31sub new { 278sub new {
32 my ($class, %kv) = @_; 279 my ($class, %kv) = @_;
33 280
34 bless { 281 bless {
35 mpv => "mpv", 282 mpv => "mpv",
36 args => [], 283 args => [],
37 %kv, 284 %kv,
38 }, $class 285 }, $class
39} 286}
287
288=item $string = $mpv->escape_binary ($string)
289
290This module excects all command data sent to F<mpv> to be in unicode. Some
291things are not, such as filenames. To pass binary data such as filenames
292through a comamnd, you need to escape it using this method.
293
294The simplest example is a C<loadfile> command:
295
296 $mpv->cmd_recv (loadfile => $mpv->escape_binary ($path));
297
298=cut
40 299
41# can be used to escape filenames 300# can be used to escape filenames
42sub escape_binary { 301sub escape_binary {
43 shift; 302 shift;
44 local $_ = shift; 303 local $_ = shift;
45 # we escape every "illegal" octet using U+10e5df HEX. this is later undone in cmd 304 # we escape every "illegal" octet using U+10e5df HEX. this is later undone in cmd
46 s/([\x00-\x1f\x80-\xff])/sprintf "\x{10e5df}%02x", ord $1/ge; 305 s/([\x00-\x1f\x80-\xff])/sprintf "\x{10e5df}%02x", ord $1/ge;
47 $_ 306 $_
48} 307}
49 308
309=item $started = $mpv->start (argument...)
310
311Starts F<mpv>, passing the given arguemnts as extra arguments to
312F<mpv>. If F<mpv> is already running, it returns false, otherwise it
313returns a true value, so you can easily start F<mpv> on demand by calling
314C<start> just before using it, and if it is already running, it will not
315be started again.
316
317The arguments passwd to F<mpv> are a set of hardcoded built-in arguments,
318followed by the arguments specified in the constructor, followed by the
319arguments passwd to this method. The built-in arguments currently are
320F<--no-input-terminal>, F<--really-quiet> (or F<--quiet> in C<trace>
321mode), and C<--input-ipc-client> (or equivalent).
322
323Some 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
328don'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
331inspect/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
343The 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
351You can immediately starting sending commands when this method returns,
352even if F<mpv> has not yet started.
353
354=cut
355
50sub start { 356sub start {
51 my ($self, @extra_args) = @_; 357 my ($self, @extra_args) = @_;
52 358
53 return if $self->{fh}; 359 return 0 if $self->{fh};
54 360
55 # cache optionlist for same "path" 361 # cache optionlist for same "path"
56 ($mpv_path, $mpv_optionlist) = ($self->{mpv}, qx{\Q$self->{$mpv_path}\E --list-options}) 362 ($mpv_path, $mpv_optionlist) = ($self->{mpv}, scalar qx{\Q$self->{mpv}\E --list-options})
57 if $self->{mpv} ne $mpv_path; 363 if $self->{mpv} ne $mpv_path;
58 364
59 my $options = $mpv_optionlist; 365 my $options = $mpv_optionlist;
60 366
61 my ($fh, $slave) = AnyEvent::Util::portable_socketpair 367 my ($fh, $slave) = AnyEvent::Util::portable_socketpair
62 or die "socketpair: $!\n"; 368 or die "socketpair: $!\n";
63 369
64 fh_nonblocking $fh , 1; 370 AnyEvent::Util::fh_nonblocking $fh, 1;
65 fh_nonblocking $slave, 0;
66 371
67 my $self->{pid} = fork; 372 $self->{pid} = fork;
68 373
69 if ($self->{pid} eq 0) { 374 if ($self->{pid} eq 0) {
375 AnyEvent::Util::fh_nonblocking $slave, 0;
376 fcntl $slave, Fcntl::F_SETFD, 0;
377
70 my $input_file = $options =~ /\s--input-ipc-client\s/ ? "input-ipc-client" : "input-file"; 378 my $input_file = $options =~ /\s--input-ipc-client\s/ ? "input-ipc-client" : "input-file";
71 379
72 exec $self->{mpv}, 380 exec $self->{mpv},
73 qw(--no-input-terminal --idle=yes --pause), 381 qw(--no-input-terminal),
74 ($self->{trace} ? "--quiet" : "--really-quiet"), 382 ($self->{trace} ? "--quiet" : "--really-quiet"),
75 "--$input_file=fd://" . (fileno $slave), 383 "--$input_file=fd://" . (fileno $slave),
76 @{ $self->{args} }, 384 @{ $self->{args} },
77 @extra_args; 385 @extra_args;
78 exit 1; 386 exit 1;
79 } 387 }
80 388
81 $self->{fh} = $fh; 389 $self->{fh} = $fh;
82 390
83 my $trace = $self->{trace} || sub { }; 391 my $trace = delete $self->{trace} || sub { };
84 392
85 $trace = sub { warn "$_[0] $_[1]\n" } if $trace && !ref $trace; 393 $trace = sub { warn "$_[0] $_[1]\n" } if $trace && !ref $trace;
86 394
87 my $buf; 395 my $buf;
88 my $rw; 396
89 my $ww; 397 Scalar::Util::weaken $self;
90 my $wbuf;
91 my $reqid;
92 398
93 $self->{rw} = AE::io $fh, 0, sub { 399 $self->{rw} = AE::io $fh, 0, sub {
94 if (sysread $fh, $buf, 8192, length $buf) { 400 if (sysread $fh, $buf, 8192, length $buf) {
95 while ($buf =~ s/^([^\n]+)\n//) { 401 while ($buf =~ s/^([^\n]+)\n//) {
96 $trace->("mpv>" => "$1"); 402 $trace->("mpv>" => "$1");
97 403
98 if ("{" eq substr $1, 0, 1) { 404 if ("{" eq substr $1, 0, 1) {
99 eval { 405 eval {
100 my $reply = JSON::XS->new->latin1->decode ($1); 406 my $reply = $JSON_DECODER->decode ($1);
101 407
102 if (exists $reply->{event}) { 408 if (defined (my $event = delete $reply->{event})) {
103 if ( 409 if (
104 $reply->{event} eq "client-message" 410 $event eq "client-message"
105 and $reply->{args}[0] eq "AnyEvent::MPV" 411 and $reply->{args}[0] eq "AnyEvent::MPV"
106 and $reply->{args}[1] eq "keyhack"
107 ) { 412 ) {
413 if ($reply->{args}[1] eq "key") {
414 (my $key = $reply->{args}[2]) =~ s/\\x(..)/chr hex $1/ge;
108 $self->on_key_event ($reply->{args}[2]); 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 }
109 } else { 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
110 $self->on_event ($reply->{event}); 433 $self->on_event ($event, $reply);
111 } 434 }
112 } elsif (exists $reply->{request_id}) { 435 } elsif (exists $reply->{request_id}) {
113 my $cv = delete $self->{cmd_cv}{$reply->{request_id}}; 436 my $cv = delete $self->{cmdcv}{$reply->{request_id}};
114 437
115 unless ($cv) { 438 unless ($cv) {
116 warn "no cv found for request id <$reply->{request_id}>\n"; 439 warn "no cv found for request id <$reply->{request_id}>\n";
117 next; 440 next;
118 } 441 }
133 } else { 456 } else {
134 $trace->("mpv>" => "$1"); 457 $trace->("mpv>" => "$1");
135 } 458 }
136 } 459 }
137 } else { 460 } else {
461 $self->stop;
138 $self->on_eof; 462 $self->on_eof;
139 } 463 }
140 }; 464 };
141 465
466 my $wbuf;
467 my $reqid;
468
142 $self->{_send} = sub { 469 $self->{_cmd} = sub {
143 $wbuf .= "$_[0]\n"; 470 my $cv = AE::cv;
144 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
145 $trace->(">mpv" => "$_[0]"); 479 $trace->(">mpv" => $cmd);
480
481 $wbuf .= "$cmd\n";
146 482
147 $self->{ww} ||= AE::io $fh, 1, sub { 483 $self->{ww} ||= AE::io $fh, 1, sub {
148 my $len = syswrite $fh, $wbuf; 484 my $len = syswrite $fh, $wbuf;
149 substr $wbuf, 0, $len, ""; 485 substr $wbuf, 0, $len, "";
150 undef $self->{ww} unless length $wbuf; 486 undef $self->{ww} unless length $wbuf;
151 }; 487 };
488
489 $cv
152 }; 490 };
491
492 1
153} 493}
494
495sub DESTROY {
496 $_[0]->stop;
497}
498
499=item $mpv->stop
500
501Ensures that F<mpv> is being stopped, by killing F<mpv> with a C<TERM>
502signal if needed. After this, you can C<< ->start >> a new instance again.
503
504=cut
505
506sub 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
531This method is called when F<mpv> quits - usually unexpectedly. The
532default implementation will call the C<on_eof> code reference specified in
533the constructor, or do nothing if none was given.
534
535For subclassing, see I<SUBCLASSING>, below.
536
537=cut
154 538
155sub on_eof { 539sub on_eof {
156 my ($self) = @_; 540 my ($self) = @_;
157 541
158 $self->{on_eof}($self) if $self->{on_eof}; 542 $self->{on_eof}($self) if $self->{on_eof};
159} 543}
160 544
545=item $mpv->on_event ($event, $data)
546
547This method is called when F<mpv> sends an asynchronous event. The default
548implementation will call the C<on_event> code reference specified in the
549constructor, or do nothing if none was given.
550
551The first/implicit argument is the C<$mpv> object, the second is the
552event name (same as C<< $data->{event} >>, purely for convenience), and
553the third argument is the event object as sent by F<mpv> (sans C<event>
554key). See L<List of events|https://mpv.io/manual/stable/#list-of-events>
555in its documentation.
556
557For subclassing, see I<SUBCLASSING>, below.
558
559=cut
560
161sub on_event { 561sub on_event {
162 my ($self, $key) = @_; 562 my ($self, $key) = @_;
163 563
164 $self->{on_event}($self, $key) if $self->{on_event}; 564 $self->{on_event}($self, $key) if $self->{on_event};
165} 565}
166 566
567=item $mpv->on_key ($string)
568
569Invoked when a key declared by C<< ->bind_key >> is pressed. The default
570invokes the C<on_key> code reference specified in the constructor with the
571C<$mpv> object and the key name as arguments, or do nothing if none was
572given.
573
574For more details and examples, see the C<bind_key> method.
575
576For subclassing, see I<SUBCLASSING>, below.
577
578=cut
579
167sub on_key_event { 580sub on_key {
168 my ($self, $key) = @_; 581 my ($self, $key) = @_;
169 582
170 $self->{on_key_event}($self, $key) if $self->{on_key_event}; 583 $self->{on_key}($self, $key) if $self->{on_key};
171} 584}
585
586=item $mpv->cmd ($command => $arg, $arg...)
587
588Queues a command to be sent to F<mpv>, using the given arguments, and
589immediately return a condvar.
590
591See L<the mpv
592documentation|https://mpv.io/manual/stable/#list-of-input-commands> for
593details on individual commands.
594
595The condvar can be ignored:
596
597 $mpv->cmd (set_property => "deinterlace", "yes");
598
599Or 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
612Or 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
619On error, the condvar will croak when C<recv> is called.
620
621=cut
172 622
173sub cmd { 623sub cmd {
624 my $self = shift;
625
626 $self->{_cmd}->(@_)
627}
628
629=item $result = $mpv->cmd_recv ($command => $arg, $arg...)
630
631The same as calling C<cmd> and immediately C<recv> on its return
632value. Useful when you don't want to mess with F<mpv> asynchronously or
633simply needs to have the result:
634
635 $mpv->cmd_recv ("stop");
636 $position = $mpv->cmd_recv ("get_property", "playback-time");
637
638=cut
639
640sub cmd_recv {
641 &cmd->recv
642}
643
644=item $mpv->bind_key ($INPUT => $string)
645
646This is an extension implement by this module to make it easy to get key
647events. The way this is implemented is to bind a C<client-message> witha
648first argument of C<AnyEvent::MPV> and the C<$string> you passed. This
649C<$string> is then passed to the C<on_key> handle when the key is
650proessed, 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
664You cna find a list of key names L<in the mpv
665documentation|https://mpv.io/manual/stable/#key-names>.
666
667The key configuration is lost when F<mpv> is stopped and must be (re-)done
668after every C<start>.
669
670=cut
671
672sub 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
681This method registers a callback to be invoked for a specific
682event. Whenever the event occurs, it calls the coderef with the C<$mpv>
683object, the C<$event> name and the event object, just like the C<on_event>
684method.
685
686For a lst of events, see L<the mpv
687documentation|https://mpv.io/manual/stable/#list-of-events>. Any
688underscore in the event name is replaced by a minus sign, so you can
689specify event names using underscores for easier quoting in Perl.
690
691In void context, the handler stays registered until C<stop> is called. In
692any other context, it returns a guard object that, when destroyed, will
693unregister the handler.
694
695You can register multiple handlers for the same event, and this method
696does not interfere with the C<on_event> mechanism. That is, you can
697completely ignore this method and handle events in a C<on_event> handler,
698or mix both approaches as you see fit.
699
700=cut
701
702sub AnyEvent::MPV::Unevent::DESTROY {
703 my ($evtcb, $event, $evtid) = @{$_[0]};
704 delete $evtcb->{$event}{$evtid};
705}
706
707sub register_event {
174 my ($self, @cmd) = @_; 708 my ($self, $event, $cb) = @_;
175 709
176 my $cv = AE::cv; 710 $event =~ y/_/-/;
177 711
178 my $reqid = ++$self->{reqid}; 712 my $evtid = ++$self->{evtid};
179 $self->{cmd_cv}{$reqid} = $cv; 713 $self->{evtcb}{$event}{$evtid} = $cb;
180 714
181 my $cmd = JSON::XS::encode_json { command => ref $cmd[0] ? $cmd[0] : \@cmd, request_id => $reqid*1 }; 715 defined wantarray
182 716 and bless [$self->{evtcb}, $event, $evtid], AnyEvent::MPV::Unevent::
183 # (un-)apply escape_binary hack
184 $cmd =~ s/\xf4\x8e\x97\x9f(..)/sprintf sprintf "\\x%02x", hex $1/ges; # f48e979f == 10e5df in utf-8
185
186 $self->{_send}($cmd);
187
188 $cv
189} 717}
190 718
191sub stop { 719=item [$guard] = $mpv->observe_property ($name => $coderef->($mpv, $name, $value))
720
721=item [$guard] = $mpv->observe_property_string ($name => $coderef->($mpv, $name, $value))
722
723These methods wrap a registry system around F<mpv>'s C<observe_property>
724and C<observe_property_string> commands - every time the named property
725changes, the coderef is invoked with the C<$mpv> object, the name of the
726property and the new value.
727
728For a list of properties that you can observe, see L<the mpv
729documentation|https://mpv.io/manual/stable/#property-list>.
730
731Due to the (sane :) way F<mpv> handles these requests, you will always
732get a property cxhange event right after registering an observer (meaning
733you don't have to query the current value), and it is also possible to
734register multiple observers for the same property - they will all be
735handled properly.
736
737When called in void context, the observer stays in place until F<mpv>
738is stopped. In any otrher context, these methods return a guard
739object that, when it goes out of scope, unregisters the observe using
740C<unobserve_property>.
741
742Internally, this method uses observer ids of 2**52 (0x10000000000000) or
743higher - it will not interfere with lower ovserver ids, so it is possible
744to completely ignore this system and execute C<observe_property> commands
745yourself, whilst listening to C<property-change> events - as long as your
746ids stay below 2**52.
747
748Example: register observers for changtes in C<aid> and C<sid>. Note that
749a dummy statement is added to make sure the method is called in void
750context.
751
752 sub register_observers {
192 my ($self) = @_; 753 my ($mpv) = @_;
193 754
194 if ($self->{pid}) { 755 $mpv->observe_property (aid => sub {
195 delete $self->{rw}; 756 my ($mpv, $name, $value) = @_;
196 delete $self->{ww}; 757 print "property aid (=$name) has changed to $value\n";
758 });
197 759
198 close delete $self->{fh}; # current mpv versions should cleanup on their own on close 760 $mpv->observe_property (sid => sub {
761 my ($mpv, $name, $value) = @_;
762 print "property sid (=$name) has changed to $value\n";
763 });
199 764
200 kill TERM => $self->{pid}; 765 () # ensure the above method is called in void context
201
202 delete $self->{pid};
203 } 766 }
767
768=cut
769
770sub AnyEvent::MPV::Unobserve::DESTROY {
771 my ($mpv, $obscb, $obsid) = @{$_[0]};
772
773 delete $obscb->{$obsid};
774
775 if ($obscb == $mpv->{obscb}) {
776 $mpv->cmd (unobserve_property => $obsid+0);
777 }
204} 778}
779
780sub _observe_property {
781 my ($self, $type, $property, $cb) = @_;
782
783 my $obsid = OBSID + ++$self->{obsid};
784 $self->cmd ($type => $obsid+0, $property);
785 $self->{obscb}{$obsid} = $cb;
786
787 defined wantarray and do {
788 my $unobserve = bless [$self, $self->{obscb}, $obsid], AnyEvent::MPV::Unobserve::;
789 Scalar::Util::weaken $unobserve->[0];
790 $unobserve
791 }
792}
793
794sub observe_property {
795 my ($self, $property, $cb) = @_;
796
797 $self->_observe_property (observe_property => $property, $cb)
798}
799
800sub observe_property_string {
801 my ($self, $property, $cb) = @_;
802
803 $self->_observe_property (observe_property_string => $property, $cb)
804}
805
806=back
807
808=head2 SUBCLASSING
809
810Like most perl objects, C<AnyEvent::MPV> objects are implemented as
811hashes, with the constructor simply storing all passed key-value pairs in
812the object. If you want to subclass to provide your own C<on_*> methods,
813be my guest and rummage around in the internals as much as you wish - the
814only guarantee that this module dcoes is that it will not use keys with
815double colons in the name, so youc an use those, or chose to simply not
816care and deal with the breakage.
817
818If you don't want to go to the effort of subclassing this module, you can
819also specify all event handlers as constructor keys.
205 820
206=head1 SEE ALSO 821=head1 SEE ALSO
207 822
208L<AnyEvent>. 823L<AnyEvent>, L<the mpv command documentation|https://mpv.io/manual/stable/#command-interface>.
209 824
210=head1 AUTHOR 825=head1 AUTHOR
211 826
212 Marc Lehmann <schmorp@schmorp.de> 827 Marc Lehmann <schmorp@schmorp.de>
213 http://home.schmorp.de/ 828 http://home.schmorp.de/

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines