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.8 by root, Mon Mar 20 11:12: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 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
18use common::sense; 206use common::sense;
207
208use Fcntl ();
209use Scalar::Util ();
19 210
20use AnyEvent (); 211use AnyEvent ();
21use AnyEvent::Util (); 212use AnyEvent::Util ();
22 213
23our $JSON = eval { require JSON::XS; JSON::XS:: } 214our $JSON = eval { require JSON::XS; JSON::XS:: }
24 || do { require JSON::PP; JSON::PP:: }; 215 || do { require JSON::PP; JSON::PP:: };
25 216
217our $JSON_CODER =
218
26our $VERSION = '0.1'; 219our $VERSION = '0.1';
27 220
28our $mpv_path; # last mpv path used 221our $mpv_path; # last mpv path used
29our $mpv_optionlist; # output of mpv --list-options 222our $mpv_optionlist; # output of mpv --list-options
223
224=item $mpv = AnyEvent::MPV->new (key => value...)
225
226Creates a new C<mpv> object, but does not yet do anything. The support key-value pairs are:
227
228=over
229
230=item mpv => $path
231
232The path to the F<mpv> binary to use - by default, C<mpv> is used and
233therefore, uses your C<PATH> to find it.
234
235=item args => [...]
236
237Arguments to pass to F<mpv>. These arguments are passed after the
238hardcoded arguments used by this module, but before the arguments passed
239ot C<start>. It does not matter whether you specify your arguments using
240this key, or in the C<start> call, but when you invoke F<mpv> multiple
241times, typically the arguments used for all invocations go here, while
242arguments used for specific invocations (e..g filenames) are passed to
243C<start>.
244
245=item trace => false|true|coderef
246
247Enables tracing if true. In trace mode, output from F<mpv> is printed to
248standard error using a C<< mpv> >> prefix, and commands sent to F<mpv>
249are printed with a C<< >mpv >> prefix.
250
251If a code reference is passed, then instead of printing to standard
252errort, this coderef is invoked with a first arfgument being either
253C<< mpv> >> or C<< >mpv >>, and the second argument being a string to
254display. The default implementation simply does this:
255
256 sub {
257 warn "$_[0] $_[1]\n";
258 }
259
260=item on_eof => $coderef->($mpv)
261
262=item on_event => $coderef->($mpv, $event, $data)
263
264=item on_key => $coderef->($mpv, $string)
265
266These are invoked by the default method implementation of the same name -
267see below.
268
269=back
270
271=cut
30 272
31sub new { 273sub new {
32 my ($class, %kv) = @_; 274 my ($class, %kv) = @_;
33 275
34 bless { 276 bless {
35 mpv => "mpv", 277 mpv => "mpv",
36 args => [], 278 args => [],
37 %kv, 279 %kv,
38 }, $class 280 }, $class
39} 281}
282
283=item $string = $mpv->escape_binary ($string)
284
285This module excects all command data sent to F<mpv> to be in unicode. Some
286things are not, such as filenames. To pass binary data such as filenames
287through a comamnd, you need to escape it using this method.
288
289The simplest example is a C<loadfile> command:
290
291 $mpv->cmd_recv (loadfile => $mpv->escape_binary ($path));
292
293=cut
40 294
41# can be used to escape filenames 295# can be used to escape filenames
42sub escape_binary { 296sub escape_binary {
43 shift; 297 shift;
44 local $_ = shift; 298 local $_ = shift;
45 # we escape every "illegal" octet using U+10e5df HEX. this is later undone in cmd 299 # 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; 300 s/([\x00-\x1f\x80-\xff])/sprintf "\x{10e5df}%02x", ord $1/ge;
47 $_ 301 $_
48} 302}
49 303
304=item $started = $mpv->start (argument...)
305
306Starts F<mpv>, passing the given arguemnts as extra arguments to
307F<mpv>. If F<mpv> is already running, it returns false, otherwise it
308returns a true value, so you can easily start F<mpv> on demand by calling
309C<start> just before using it, and if it is already running, it will not
310be started again.
311
312The arguments passwd to F<mpv> are a set of hardcoded built-in arguments,
313followed by the arguments specified in the constructor, followed by the
314arguments passwd to this method. The built-in arguments currently are
315F<--no-input-terminal>, F<--really-quiet> (or F<--quiet> in C<trace>
316mode), and C<--input-ipc-client> (or equivalent).
317
318Some commonly used and/or even useful arguments you might want to pass are:
319
320=over
321
322=item F<--idle=yes> or F<--idle=once> to keep F<mpv> from quitting when you
323don't specify a file to play.
324
325=item F<--pause>, to keep F<mpv> from instantly starting to play a file, in case you want to
326inspect/change properties first.
327
328=item F<--force-window=no> (or similar), to keep F<mpv> from instantly opening a window, or to force it to do so.
329
330=item F<--audio-client-name=yourappname>, to make sure audio streams are associated witht eh right program.
331
332=item F<--wid=id>, to embed F<mpv> into another application.
333
334=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.
335
336=back
337
338The return value can be used to decide whether F<mpv> needs initializing:
339
340 if ($mpv->start) {
341 $mpv->bind_key (...);
342 $mpv->cmd (set => property => value);
343 ...
344 }
345
346You can immediately starting sending commands when this method returns,
347even if F<mpv> has not yet started.
348
349=cut
350
50sub start { 351sub start {
51 my ($self, @extra_args) = @_; 352 my ($self, @extra_args) = @_;
52 353
53 return if $self->{fh}; 354 return 0 if $self->{fh};
54 355
55 # cache optionlist for same "path" 356 # cache optionlist for same "path"
56 ($mpv_path, $mpv_optionlist) = ($self->{mpv}, qx{\Q$self->{$mpv_path}\E --list-options}) 357 ($mpv_path, $mpv_optionlist) = ($self->{mpv}, scalar qx{\Q$self->{mpv}\E --list-options})
57 if $self->{mpv} ne $mpv_path; 358 if $self->{mpv} ne $mpv_path;
58 359
59 my $options = $mpv_optionlist; 360 my $options = $mpv_optionlist;
60 361
61 my ($fh, $slave) = AnyEvent::Util::portable_socketpair 362 my ($fh, $slave) = AnyEvent::Util::portable_socketpair
62 or die "socketpair: $!\n"; 363 or die "socketpair: $!\n";
63 364
64 fh_nonblocking $fh , 1; 365 AnyEvent::Util::fh_nonblocking $fh, 1;
65 fh_nonblocking $slave, 0;
66 366
67 my $self->{pid} = fork; 367 $self->{pid} = fork;
68 368
69 if ($self->{pid} eq 0) { 369 if ($self->{pid} eq 0) {
370 AnyEvent::Util::fh_nonblocking $slave, 0;
371 fcntl $slave, Fcntl::F_SETFD, 0;
372
70 my $input_file = $options =~ /\s--input-ipc-client\s/ ? "input-ipc-client" : "input-file"; 373 my $input_file = $options =~ /\s--input-ipc-client\s/ ? "input-ipc-client" : "input-file";
71 374
72 exec $self->{mpv}, 375 exec $self->{mpv},
73 qw(--no-input-terminal --idle=yes --pause), 376 qw(--no-input-terminal),
74 ($self->{trace} ? "--quiet" : "--really-quiet"), 377 ($self->{trace} ? "--quiet" : "--really-quiet"),
75 "--$input_file=fd://" . (fileno $slave), 378 "--$input_file=fd://" . (fileno $slave),
76 @{ $self->{args} }, 379 @{ $self->{args} },
77 @extra_args; 380 @extra_args;
78 exit 1; 381 exit 1;
79 } 382 }
80 383
81 $self->{fh} = $fh; 384 $self->{fh} = $fh;
82 385
83 my $trace = $self->{trace} || sub { }; 386 my $trace = delete $self->{trace} || sub { };
84 387
85 $trace = sub { warn "$_[0] $_[1]\n" } if $trace && !ref $trace; 388 $trace = sub { warn "$_[0] $_[1]\n" } if $trace && !ref $trace;
86 389
87 my $buf; 390 my $buf;
88 my $rw; 391
89 my $ww; 392 Scalar::Util::weaken $self;
90 my $wbuf;
91 my $reqid;
92 393
93 $self->{rw} = AE::io $fh, 0, sub { 394 $self->{rw} = AE::io $fh, 0, sub {
94 if (sysread $fh, $buf, 8192, length $buf) { 395 if (sysread $fh, $buf, 8192, length $buf) {
95 while ($buf =~ s/^([^\n]+)\n//) { 396 while ($buf =~ s/^([^\n]+)\n//) {
96 $trace->("mpv>" => "$1"); 397 $trace->("mpv>" => "$1");
97 398
98 if ("{" eq substr $1, 0, 1) { 399 if ("{" eq substr $1, 0, 1) {
99 eval { 400 eval {
100 my $reply = JSON::XS->new->latin1->decode ($1); 401 my $reply = $JSON->new->latin1->decode ($1);
101 402
102 if (exists $reply->{event}) { 403 if (exists $reply->{event}) {
103 if ( 404 if (
104 $reply->{event} eq "client-message" 405 $reply->{event} eq "client-message"
105 and $reply->{args}[0] eq "AnyEvent::MPV" 406 and $reply->{args}[0] eq "AnyEvent::MPV"
106 and $reply->{args}[1] eq "keyhack"
107 ) { 407 ) {
408 if ($reply->{args}[1] eq "key") {
409 (my $key = $reply->{args}[2]) =~ s/\\x(..)/chr hex $1/ge;
108 $self->on_key_event ($reply->{args}[2]); 410 $self->on_key ($key);
411 }
109 } else { 412 } else {
110 $self->on_event ($reply->{event}); 413 $self->on_event (delete $reply->{event}, $reply);
111 } 414 }
112 } elsif (exists $reply->{request_id}) { 415 } elsif (exists $reply->{request_id}) {
113 my $cv = delete $self->{cmd_cv}{$reply->{request_id}}; 416 my $cv = delete $self->{cmd_cv}{$reply->{request_id}};
114 417
115 unless ($cv) { 418 unless ($cv) {
133 } else { 436 } else {
134 $trace->("mpv>" => "$1"); 437 $trace->("mpv>" => "$1");
135 } 438 }
136 } 439 }
137 } else { 440 } else {
441 $self->stop;
138 $self->on_eof; 442 $self->on_eof;
139 } 443 }
140 }; 444 };
141 445
446 my $wbuf;
447 my $reqid;
448
142 $self->{_send} = sub { 449 $self->{_cmd} = sub {
450 my $cv = AE::cv;
451
452 $self->{cmd_cv}{++$reqid} = $cv;
453
454 my $cmd = $JSON->new->utf8->encode ({ command => ref $_[0] ? $_[0] : \@_, request_id => $reqid*1 });
455
456 # (un-)apply escape_binary hack
457 $cmd =~ s/\xf4\x8e\x97\x9f(..)/sprintf sprintf "\\x%02x", hex $1/ges; # f48e979f == 10e5df in utf-8
458
143 $wbuf .= "$_[0]\n"; 459 $wbuf .= "$cmd\n";
144 460
145 $trace->(">mpv" => "$_[0]"); 461 $trace->(">mpv" => "$_[0]");
146 462
147 $self->{ww} ||= AE::io $fh, 1, sub { 463 $self->{ww} ||= AE::io $fh, 1, sub {
148 my $len = syswrite $fh, $wbuf; 464 my $len = syswrite $fh, $wbuf;
149 substr $wbuf, 0, $len, ""; 465 substr $wbuf, 0, $len, "";
150 undef $self->{ww} unless length $wbuf; 466 undef $self->{ww} unless length $wbuf;
151 }; 467 };
468
469 $cv
152 }; 470 };
471
472 1
153} 473}
474
475sub DESTROY {
476 $_[0]->stop;
477}
478
479=item $mpv->stop
480
481Ensures that F<mpv> is being stopped, by killing F<mpv> with a C<TERM>
482signal if needed. After this, you can C<< ->start >> a new instance again.
483
484=cut
485
486sub stop {
487 my ($self) = @_;
488
489 delete $self->{rw};
490 delete $self->{ww};
491
492 if ($self->{pid}) {
493
494 close delete $self->{fh}; # current mpv versions should cleanup on their own on close
495
496 kill TERM => $self->{pid};
497
498 }
499
500 delete $self->{pid};
501 delete $self->{cmd_cv};
502 delete $self->{obsid};
503 delete $self->{wbuf};
504}
505
506=item $mpv->on_eof
507
508This method is called when F<mpv> quits - usually unexpectedly. The
509default implementation will call the C<on_eof> code reference specified in
510the constructor, or do nothing if none was given.
511
512For subclassing, see I<SUBCLASSING>, below.
513
514=cut
154 515
155sub on_eof { 516sub on_eof {
156 my ($self) = @_; 517 my ($self) = @_;
157 518
158 $self->{on_eof}($self) if $self->{on_eof}; 519 $self->{on_eof}($self) if $self->{on_eof};
159} 520}
160 521
522=item $mpv->on_event ($event, $data)
523
524This method is called when F<mpv> sends an asynchronous event. The default
525implementation will call the C<on_event> code reference specified in the
526constructor, or do nothing if none was given.
527
528The first/implicit argument is the C<$mpv> object, the second is the
529event name (same as C<< $data->{event} >>, purely for convenience), and
530the third argument is the event object as sent by F<mpv> (sans C<event>
531key). See L<List of events|https://mpv.io/manual/stable/#list-of-events>
532in its documentation.
533
534For subclassing, see I<SUBCLASSING>, below.
535
536=cut
537
161sub on_event { 538sub on_event {
162 my ($self, $key) = @_; 539 my ($self, $key) = @_;
163 540
164 $self->{on_event}($self, $key) if $self->{on_event}; 541 $self->{on_event}($self, $key) if $self->{on_event};
165} 542}
166 543
544=item $mpv->on_key ($string)
545
546Invoked when a key declared by C<< ->bind_key >> is pressed. The default
547invokes the C<on_key> code reference specified in the constructor with the
548C<$mpv> object and the key name as arguments, or do nothing if none was
549given.
550
551For more details and examples, see the C<bind_key> method.
552
553For subclassing, see I<SUBCLASSING>, below.
554
555=cut
556
167sub on_key_event { 557sub on_key {
168 my ($self, $key) = @_; 558 my ($self, $key) = @_;
169 559
170 $self->{on_key_event}($self, $key) if $self->{on_key_event}; 560 $self->{on_key}($self, $key) if $self->{on_key};
171} 561}
562
563=item $mpv->cmd ($command => $arg, $arg...)
564
565Queues a command to be sent to F<mpv>, using the given arguments, and
566immediately return a condvar.
567
568See L<the mpv
569documentation|https://mpv.io/manual/stable/#list-of-input-commands> for
570details on individual commands.
571
572The condvar can be ignored:
573
574 $mpv->cmd (set_property => "deinterlace", "yes");
575
576Or it can be used to synchronously wait for the command results:
577
578 $cv = $mpv->cmd (get_property => "video-format");
579 $format = $cv->recv;
580
581 # or simpler:
582
583 $format = $mpv->cmd (get_property => "video-format")->recv;
584
585 # or even simpler:
586
587 $format = $mpv->cmd_recv (get_property => "video-format");
588
589Or you can set a callback:
590
591 $cv = $mpv->cmd (get_property => "video-format");
592 $cv->cb (sub {
593 my $format = $_[0]->recv;
594 });
595
596On error, the condvar will croak when C<recv> is called.
597
598=cut
172 599
173sub cmd { 600sub cmd {
174 my ($self, @cmd) = @_;
175
176 my $cv = AE::cv;
177
178 my $reqid = ++$self->{reqid};
179 $self->{cmd_cv}{$reqid} = $cv;
180
181 my $cmd = JSON::XS::encode_json { command => ref $cmd[0] ? $cmd[0] : \@cmd, request_id => $reqid*1 };
182
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}
190
191sub stop {
192 my ($self) = @_; 601 my $self = shift;
193 602
194 if ($self->{pid}) { 603 $self->{_cmd}->(@_)
195 delete $self->{rw};
196 delete $self->{ww};
197
198 close delete $self->{fh}; # current mpv versions should cleanup on their own on close
199
200 kill TERM => $self->{pid};
201
202 delete $self->{pid};
203 }
204} 604}
605
606=item $result = $mpv->cmd_recv ($command => $arg, $arg...)
607
608The same as calling C<cmd> and immediately C<recv> on its return
609value. Useful when you don't want to mess with F<mpv> asynchronously or
610simply needs to have the result:
611
612 $mpv->cmd_recv ("stop");
613 $position = $mpv->cmd_recv ("get_property", "playback-time");
614
615=cut
616
617sub cmd_recv {
618 &cmd->recv
619}
620
621=item $mpv->bind_key ($INPUT => $string)
622
623This is an extension implement by this module to make it easy to get key events. The way this is implemented
624is to bind a C<client-message> witha first argument of C<AnyEvent::MPV> and the C<$string> you passed. This C<$string> is then
625passed ot the C<on_key> handle when the key is proessed, e.g.:
626
627 my $mpv = AnyEvent::MPV->new (
628 on_key => sub {
629 my ($mpv, $key) = @_;
630
631 if ($key eq "letmeout") {
632 print "user pressed escape\n";
633 }
634 },
635 );
636
637 $mpv_>bind_key (ESC => "letmeout");
638
639The key configuration is lost when F<mpv> is stopped and must be (re-)done
640after every C<start>.
641
642=cut
643
644sub bind_key {
645 my ($self, $key, $event) = @_;
646
647 $event =~ s/([^A-Za-z0-9\-_])/sprintf "\\x%02x", ord $1/ge;
648 $self->cmd (keybind => $key => "no-osd script-message AnyEvent::MPV key $event");
649}
650
651=back
652
653=head2 SUBCLASSING
654
655Like most perl objects, C<AnyEvent::MPV> objects are implemented as
656hashes, with the constructor simply storing all passed key-value pairs in
657the object. If you want to subclass to provide your own C<on_*> methods,
658be my guest and rummage around in the internals as much as you wish - the
659only guarantee that this module dcoes is that it will not use keys with
660double colons in the name, so youc an use those, or chose to simply not
661care and deal with the breakage.
662
663If you don't want to go to the effort of subclassing this module, you can
664also specify all event handlers as constructor keys.
205 665
206=head1 SEE ALSO 666=head1 SEE ALSO
207 667
208L<AnyEvent>. 668L<AnyEvent>, L<the mpv command documentation|https://mpv.io/manual/stable/#command-interface>.
209 669
210=head1 AUTHOR 670=head1 AUTHOR
211 671
212 Marc Lehmann <schmorp@schmorp.de> 672 Marc Lehmann <schmorp@schmorp.de>
213 http://home.schmorp.de/ 673 http://home.schmorp.de/

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines