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.2 by root, Sun Mar 19 20:30:48 2023 UTC vs.
Revision 1.10 by root, Mon Mar 20 12:31:03 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 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
169This example uses a global condvar C<$quit> to wait for the file to finish
170playing. Also, most of the logic is now in an C<on_event> callback, which
171receives an event name and the actual event object.
172
173The two events we handle are C<start-file>, which is emitted by F<mpv>
174once it has loaded a new file, and C<end-file>, which signals the end
175of a file.
176
177In the former event, we again set the C<pause> property to C<no> so the
178movie starts playing. For the latter event, we tell the main program to
179quit by invoking C<$quit>.
180
181This should conclude the basics of operation. There are a few more
182examples later in the documentation.
183
184=head2 ENCODING CONVENTIONS
185
186As a rule of thumb, all data you pass to this module to be sent to F<mpv>
187is expected to be in unicode. To pass something that isn't, you need to
188escape it using C<escape_binary>.
189
190Data received from C<$mpv>, however, is I<not> decoded to unicode, as data
191returned by F<mpv> is not generally encoded in unicode, and the encoding
192is usually unspecified. So if you receive data and expect it to be in
193unicode, you need to first decode it from UTF-8, but note that this might
194fail. This is not a limitation of this module - F<mpv> simply does not
195specify nor guarantee a specific encoding, or any encoding at all, in its
196protocol.
197
198=head2 METHODS
199
200=over
13 201
14=cut 202=cut
15 203
16package AnyEvent::MPV; 204package AnyEvent::MPV;
17 205
21use Scalar::Util (); 209use Scalar::Util ();
22 210
23use AnyEvent (); 211use AnyEvent ();
24use AnyEvent::Util (); 212use AnyEvent::Util ();
25 213
214our $VERSION = '0.2';
215
216sub OBSID() { 0x10000000000000 } # 2**52
217
26our $JSON = eval { require JSON::XS; JSON::XS:: } 218our $JSON = eval { require JSON::XS; JSON::XS:: }
27 || do { require JSON::PP; JSON::PP:: }; 219 || do { require JSON::PP; JSON::PP:: };
28 220
29our $VERSION = '0.1'; 221our $JSON_CODER =
30 222
31our $mpv_path; # last mpv path used 223our $mpv_path; # last mpv path used
32our $mpv_optionlist; # output of mpv --list-options 224our $mpv_optionlist; # output of mpv --list-options
225
226=item $mpv = AnyEvent::MPV->new (key => value...)
227
228Creates 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
234The path to the F<mpv> binary to use - by default, C<mpv> is used and
235therefore, uses your C<PATH> to find it.
236
237=item args => [...]
238
239Arguments to pass to F<mpv>. These arguments are passed after the
240hardcoded arguments used by this module, but before the arguments passed
241ot C<start>. It does not matter whether you specify your arguments using
242this key, or in the C<start> call, but when you invoke F<mpv> multiple
243times, typically the arguments used for all invocations go here, while
244arguments used for specific invocations (e..g filenames) are passed to
245C<start>.
246
247=item trace => false|true|coderef
248
249Enables tracing if true. In trace mode, output from F<mpv> is printed to
250standard error using a C<< mpv> >> prefix, and commands sent to F<mpv>
251are printed with a C<< >mpv >> prefix.
252
253If a code reference is passed, then instead of printing to standard
254errort, this coderef is invoked with a first arfgument being either
255C<< mpv> >> or C<< >mpv >>, and the second argument being a string to
256display. 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
268These are invoked by the default method implementation of the same name -
269see below.
270
271=back
272
273=cut
33 274
34sub new { 275sub new {
35 my ($class, %kv) = @_; 276 my ($class, %kv) = @_;
36 277
37 bless { 278 bless {
38 mpv => "mpv", 279 mpv => "mpv",
39 args => [], 280 args => [],
40 %kv, 281 %kv,
41 }, $class 282 }, $class
42} 283}
284
285=item $string = $mpv->escape_binary ($string)
286
287This module excects all command data sent to F<mpv> to be in unicode. Some
288things are not, such as filenames. To pass binary data such as filenames
289through a comamnd, you need to escape it using this method.
290
291The simplest example is a C<loadfile> command:
292
293 $mpv->cmd_recv (loadfile => $mpv->escape_binary ($path));
294
295=cut
43 296
44# can be used to escape filenames 297# can be used to escape filenames
45sub escape_binary { 298sub escape_binary {
46 shift; 299 shift;
47 local $_ = shift; 300 local $_ = shift;
48 # we escape every "illegal" octet using U+10e5df HEX. this is later undone in cmd 301 # 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; 302 s/([\x00-\x1f\x80-\xff])/sprintf "\x{10e5df}%02x", ord $1/ge;
50 $_ 303 $_
51} 304}
52 305
306=item $started = $mpv->start (argument...)
307
308Starts F<mpv>, passing the given arguemnts as extra arguments to
309F<mpv>. If F<mpv> is already running, it returns false, otherwise it
310returns a true value, so you can easily start F<mpv> on demand by calling
311C<start> just before using it, and if it is already running, it will not
312be started again.
313
314The arguments passwd to F<mpv> are a set of hardcoded built-in arguments,
315followed by the arguments specified in the constructor, followed by the
316arguments passwd to this method. The built-in arguments currently are
317F<--no-input-terminal>, F<--really-quiet> (or F<--quiet> in C<trace>
318mode), and C<--input-ipc-client> (or equivalent).
319
320Some 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
325don'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
328inspect/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
340The 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
348You can immediately starting sending commands when this method returns,
349even if F<mpv> has not yet started.
350
351=cut
352
53sub start { 353sub start {
54 my ($self, @extra_args) = @_; 354 my ($self, @extra_args) = @_;
55 355
56 return if $self->{fh}; 356 return 0 if $self->{fh};
357
358 $self->{obscb} = {};
57 359
58 # cache optionlist for same "path" 360 # cache optionlist for same "path"
59 ($mpv_path, $mpv_optionlist) = ($self->{mpv}, scalar qx{\Q$self->{mpv}\E --list-options}) 361 ($mpv_path, $mpv_optionlist) = ($self->{mpv}, scalar qx{\Q$self->{mpv}\E --list-options})
60 if $self->{mpv} ne $mpv_path; 362 if $self->{mpv} ne $mpv_path;
61 363
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 (exists $reply->{event}) {
107 if ( 408 if (
108 $reply->{event} eq "client-message" 409 $reply->{event} eq "client-message"
109 and $reply->{args}[0] eq "AnyEvent::MPV" 410 and $reply->{args}[0] eq "AnyEvent::MPV"
110 and $reply->{args}[1] eq "keyhack"
111 ) { 411 ) {
412 if ($reply->{args}[1] eq "key") {
413 (my $key = $reply->{args}[2]) =~ s/\\x(..)/chr hex $1/ge;
112 $self->on_key ($reply->{args}[2]); 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 }
113 } else { 423 } else {
114 $self->on_event ($reply->{event}); 424 $self->on_event (delete $reply->{event}, $reply);
115 } 425 }
116 } elsif (exists $reply->{request_id}) { 426 } elsif (exists $reply->{request_id}) {
117 my $cv = delete $self->{cmd_cv}{$reply->{request_id}}; 427 my $cv = delete $self->{cmdcv}{$reply->{request_id}};
118 428
119 unless ($cv) { 429 unless ($cv) {
120 warn "no cv found for request id <$reply->{request_id}>\n"; 430 warn "no cv found for request id <$reply->{request_id}>\n";
121 next; 431 next;
122 } 432 }
142 $self->stop; 452 $self->stop;
143 $self->on_eof; 453 $self->on_eof;
144 } 454 }
145 }; 455 };
146 456
457 my $wbuf;
458 my $reqid;
459
147 $self->{_send} = sub { 460 $self->{_cmd} = sub {
148 $wbuf .= "$_[0]\n"; 461 my $cv = AE::cv;
149 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
150 $trace->(">mpv" => "$_[0]"); 470 $trace->(">mpv" => $cmd);
471
472 $wbuf .= "$cmd\n";
151 473
152 $self->{ww} ||= AE::io $fh, 1, sub { 474 $self->{ww} ||= AE::io $fh, 1, sub {
153 my $len = syswrite $fh, $wbuf; 475 my $len = syswrite $fh, $wbuf;
154 substr $wbuf, 0, $len, ""; 476 substr $wbuf, 0, $len, "";
155 undef $self->{ww} unless length $wbuf; 477 undef $self->{ww} unless length $wbuf;
156 }; 478 };
479
480 $cv
157 }; 481 };
482
483 1
158} 484}
485
486sub DESTROY {
487 $_[0]->stop;
488}
489
490=item $mpv->stop
491
492Ensures that F<mpv> is being stopped, by killing F<mpv> with a C<TERM>
493signal if needed. After this, you can C<< ->start >> a new instance again.
494
495=cut
496
497sub 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
520This method is called when F<mpv> quits - usually unexpectedly. The
521default implementation will call the C<on_eof> code reference specified in
522the constructor, or do nothing if none was given.
523
524For subclassing, see I<SUBCLASSING>, below.
525
526=cut
159 527
160sub on_eof { 528sub on_eof {
161 my ($self) = @_; 529 my ($self) = @_;
162 530
163 $self->{on_eof}($self) if $self->{on_eof}; 531 $self->{on_eof}($self) if $self->{on_eof};
164} 532}
165 533
534=item $mpv->on_event ($event, $data)
535
536This method is called when F<mpv> sends an asynchronous event. The default
537implementation will call the C<on_event> code reference specified in the
538constructor, or do nothing if none was given.
539
540The first/implicit argument is the C<$mpv> object, the second is the
541event name (same as C<< $data->{event} >>, purely for convenience), and
542the third argument is the event object as sent by F<mpv> (sans C<event>
543key). See L<List of events|https://mpv.io/manual/stable/#list-of-events>
544in its documentation.
545
546For subclassing, see I<SUBCLASSING>, below.
547
548=cut
549
166sub on_event { 550sub on_event {
167 my ($self, $key) = @_; 551 my ($self, $key) = @_;
168 552
169 $self->{on_event}($self, $key) if $self->{on_event}; 553 $self->{on_event}($self, $key) if $self->{on_event};
170} 554}
171 555
556=item $mpv->on_key ($string)
557
558Invoked when a key declared by C<< ->bind_key >> is pressed. The default
559invokes the C<on_key> code reference specified in the constructor with the
560C<$mpv> object and the key name as arguments, or do nothing if none was
561given.
562
563For more details and examples, see the C<bind_key> method.
564
565For subclassing, see I<SUBCLASSING>, below.
566
567=cut
568
172sub on_key { 569sub on_key {
173 my ($self, $key) = @_; 570 my ($self, $key) = @_;
174 571
175 $self->{on_key}($self, $key) if $self->{on_key}; 572 $self->{on_key}($self, $key) if $self->{on_key};
176} 573}
177 574
575=item $mpv->cmd ($command => $arg, $arg...)
576
577Queues a command to be sent to F<mpv>, using the given arguments, and
578immediately return a condvar.
579
580See L<the mpv
581documentation|https://mpv.io/manual/stable/#list-of-input-commands> for
582details on individual commands.
583
584The condvar can be ignored:
585
586 $mpv->cmd (set_property => "deinterlace", "yes");
587
588Or 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
601Or 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
608On error, the condvar will croak when C<recv> is called.
609
610=cut
611
178sub cmd { 612sub cmd {
179 my ($self, @cmd) = @_;
180
181 my $cv = AE::cv;
182
183 my $reqid = ++$self->{reqid};
184 $self->{cmd_cv}{$reqid} = $cv;
185
186 my $cmd = JSON::XS::encode_json { command => ref $cmd[0] ? $cmd[0] : \@cmd, request_id => $reqid*1 };
187
188 # (un-)apply escape_binary hack
189 $cmd =~ s/\xf4\x8e\x97\x9f(..)/sprintf sprintf "\\x%02x", hex $1/ges; # f48e979f == 10e5df in utf-8
190
191 $self->{_send}($cmd);
192
193 $cv
194}
195
196sub stop {
197 my ($self) = @_; 613 my $self = shift;
198 614
199 if ($self->{pid}) { 615 $self->{_cmd}->(@_)
200 delete $self->{rw}; 616}
201 delete $self->{ww};
202 617
203 close delete $self->{fh}; # current mpv versions should cleanup on their own on close 618=item $result = $mpv->cmd_recv ($command => $arg, $arg...)
204 619
205 kill TERM => $self->{pid}; 620The same as calling C<cmd> and immediately C<recv> on its return
621value. Useful when you don't want to mess with F<mpv> asynchronously or
622simply needs to have the result:
206 623
207 delete $self->{pid}; 624 $mpv->cmd_recv ("stop");
625 $position = $mpv->cmd_recv ("get_property", "playback-time");
626
627=cut
628
629sub cmd_recv {
630 &cmd->recv
631}
632
633=item $mpv->bind_key ($INPUT => $string)
634
635This is an extension implement by this module to make it easy to get key events. The way this is implemented
636is to bind a C<client-message> witha first argument of C<AnyEvent::MPV> and the C<$string> you passed. This C<$string> is then
637passed 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
651The key configuration is lost when F<mpv> is stopped and must be (re-)done
652after every C<start>.
653
654=cut
655
656sub 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
663sub 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);
208 } 670 }
209} 671}
210 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
677These methods wrap a registry system around F<mpv>'s C<observe_property>
678and C<observe_property_string> commands - every time the named property
679changes, the coderef is invoked with the C<$mpv> object, the name of the
680property and the new value.
681
682For a list of properties that you can observe, see L<the mpv
683documentation|https://mpv.io/manual/stable/#property-list>.
684
685Due to the (sane :) way F<mpv> handles these requests, you will always
686get a property cxhange event right after registering an observer (meaning
687you don't have to query the current value), and it is also possible to
688register multiple observers for the same property - they will all be
689handled properly.
690
691When called in void context, the observer stays in place until F<mpv>
692is stopped. In any otrher context, these methods return a guard
693object that, when it goes out of scope, unregisters the observe using
694C<unobserve_property>.
695
696Internally, this method uses observer ids of 2**52 (0x10000000000000) or
697higher - it will not interfere with lower ovserver ids, so it is possible
698to completely ignore this system and execute C<observe_property> commands
699yourself, whilst listening to C<property-change> events - as long as your
700ids stay below 2**52.
701
702Example: register observers for changtes in C<aid> and C<sid>. Note that
703a dummy statement is added to make sure the method is called in void
704context.
705
706 sub register_observers {
707 my ($mpv) = @_;
708
709 $mpv->observe_property (aid => sub {
710 my ($mpv, $name, $value) = @_;
711 print "property aid (=$name) has changed to $value\n";
712 });
713
714 $mpv->observe_property (sid => sub {
715 my ($mpv, $name, $value) = @_;
716 print "property sid (=$name) has changed to $value\n";
717 });
718
719 () # ensure the above method is called in void context
720 }
721
722=cut
723
724sub _observe_property {
725 my ($self, $type, $property, $cb) = @_;
726
727 my $obsid = OBSID + ++$self->{obsid};
728 $self->cmd ($type => $obsid+0, $property);
729 $self->{obscb}{$obsid} = $cb;
730
731 defined wantarray and do {
732 my $unobserve = bless [$self, $self->{obscb}, $obsid], AnyEvent::MPV::Unobserve::;
733 Scalar::Util::weaken $unobserve->[0];
734 $unobserve
735 }
736}
737
738sub observe_property {
739 my ($self, $property, $cb) = @_;
740
741 $self->_observe_property (observe_property => $property, $cb)
742}
743
744sub observe_property_string {
745 my ($self, $property, $cb) = @_;
746
747 $self->_observe_property (observe_property_string => $property, $cb)
748}
749
750=back
751
752=head2 SUBCLASSING
753
754Like most perl objects, C<AnyEvent::MPV> objects are implemented as
755hashes, with the constructor simply storing all passed key-value pairs in
756the object. If you want to subclass to provide your own C<on_*> methods,
757be my guest and rummage around in the internals as much as you wish - the
758only guarantee that this module dcoes is that it will not use keys with
759double colons in the name, so youc an use those, or chose to simply not
760care and deal with the breakage.
761
762If you don't want to go to the effort of subclassing this module, you can
763also specify all event handlers as constructor keys.
764
211=head1 SEE ALSO 765=head1 SEE ALSO
212 766
213L<AnyEvent>. 767L<AnyEvent>, L<the mpv command documentation|https://mpv.io/manual/stable/#command-interface>.
214 768
215=head1 AUTHOR 769=head1 AUTHOR
216 770
217 Marc Lehmann <schmorp@schmorp.de> 771 Marc Lehmann <schmorp@schmorp.de>
218 http://home.schmorp.de/ 772 http://home.schmorp.de/

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines