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.5 by root, Sun Mar 19 23:13:25 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 cna have an idea what
68is going on.
69
70This is not usually very useful (you could just run F<mpv> as a simple
71shell command), so let us load the file at runtime:
72
73 use AnyEvent;
74 use AnyEvent::MPV;
75
76 my $videofile = "./xyzzy.mp4";
77
78 my $mpv = AnyEvent::MPV->new (
79 trace => 1,
80 args => ["--pause", "--idle=yes"],
81 );
82
83 $mpv->start;
84 $mpv->cmd_recv (loadfile => $mpv->escape_binary ($videofile));
85 $mpv->cmd ("set", "pause", "no");
86
87 my $timer = AE::timer 2, 0, my $quit = AE::cv;
88 $quit->recv;
89
90This specifies extra arguments in the constructor - these arguments are
91used every time you C<< ->start >> F<mpv>, while the arguments to C<<
92->start >> are only used for this specific clal to0 C<start>. The argument
93F<--pause> keeps F<mpv> in pause mode (i.e. it does not play the file
94after loading it), and C<--idle=yes> tells F<mpv> to not quit when it does
95not have a playlist - as no files are specified on the command line.
96
97To load a file, we then send it a C<loadfile> command, which accepts, as
98first argument, the URL or path to a video file. To make sure F<mpv> does
99not misinterpret the path as a URL, it was prefixed with F<./> (similarly
100to "protecting" paths in perls C<open>).
101
102Since commands send I<to> F<mpv> are send in UTF-8, we need to escape the
103filename (which might be in any encoding) using the C<esscape_binary>
104method - this is not needed if your filenames are just ascii, or magically
105get interpreted correctly, but if you accept arbitrary filenamews (e.g.
106from the user), you need to do this.
107
108The C<cmd_recv> method then queues the command, waits for a reply and
109returns the reply data (or croaks on error). F<mpv> would, at this point,
110load the file and, if everything was successful, show the first frame and
111pause. Note that, since F<mpv> is implement rather synchronously itself,
112do not expect commands to fail in many circumstances - for example, fit
113he file does not exit, you will likely get an event, but the C<loadfile>
114command itself will run successfully.
115
116To unpause, we send another command, C<set>, to set the C<pause> property
117to C<no>, this time using the C<cmd> method, which queues the command, but
118instead of waiting for a reply, it immediately returns a condvar that cna
119be used to receive results.
120
121This should then cause F<mpv> to start playing the video.
122
123It then again waits two seconds and quits.
124
125Now, just waiting two seconds is rather, eh, unuseful, so let's look at
126receiving events (using a somewhat embellished example):
127
128 use AnyEvent;
129 use AnyEvent::MPV;
130
131 my $videofile = "xyzzy.mp4";
132
133 my $quit = AE::cv;
134
135 my $mpv = AnyEvent::MPV->new (
136 trace => 1,
137 args => ["--pause", "--idle=yes"],
138 on_event => sub {
139 my ($mpv, $event, $data) = @_;
140
141 if ($event eq "start-file") {
142 $mpv->cmd ("set", "pause", "no");
143 } elsif ($event eq "end-file") {
144 print "end-file<$data->{reason}>\n";
145 $quit->send;
146 }
147 },
148 );
149
150 $mpv->start;
151 $mpv->cmd (loadfile => $mpv->escape_binary ($videofile));
152
153 $quit->recv;
154
155This example uses a global condvar C<$quit> to wait for the file to finish
156playing. Also, most of the logic is now in an C<on_event> callback, which
157receives an event name and the actual event object.
158
159The two events we handle are C<start-file>, which is emitted by F<mpv>
160once it has loaded a new file, and C<end-file>, which signals the end
161of a file.
162
163In the former event, we again set the C<pause> property to C<no> so the
164movie starts playing. For the latter event, we tell the main program to
165quit by invoking C<$quit>.
166
167This should conclude the basics of operation. There are a few more
168examples later in the documentation.
169
170=head2 ENCODING CONVENTIONS
171
172As a rule of thumb, all data you pass to this module to be sent to F<mpv>
173is expected to be in unicode. To pass something that isn't, you need to
174escape it using C<escape_binary>.
175
176Data received from C<$mpv>, however, is I<not> decoded to unicode, as data
177returned by F<mpv> is not generally encoded in unicode, and the encoding
178is usually unspecified. So if you receive data and expect it to be in
179unicode, you need to first decode it from UTF-8, but note that this might
180fail. This is not a limitation of this module - F<mpv> simply does not
181specify nor guarantee a specific encoding, or any encoding at all, in its
182protocol.
183
184=head2 METHODS
185
186=over
13 187
14=cut 188=cut
15 189
16package AnyEvent::MPV; 190package AnyEvent::MPV;
17 191
24use AnyEvent::Util (); 198use AnyEvent::Util ();
25 199
26our $JSON = eval { require JSON::XS; JSON::XS:: } 200our $JSON = eval { require JSON::XS; JSON::XS:: }
27 || do { require JSON::PP; JSON::PP:: }; 201 || do { require JSON::PP; JSON::PP:: };
28 202
203our $JSON_CODER =
204
29our $VERSION = '0.1'; 205our $VERSION = '0.1';
30 206
31our $mpv_path; # last mpv path used 207our $mpv_path; # last mpv path used
32our $mpv_optionlist; # output of mpv --list-options 208our $mpv_optionlist; # output of mpv --list-options
209
210=item $mpv = AnyEvent::MPV->new (key => value...)
211
212Creates a new C<mpv> object, but does not yet do anything. The support key-value pairs are:
213
214=over
215
216=item mpv => $path
217
218The path to the F<mpv> binary to use - by default, C<mpv> is used and
219therefore, uses your C<PATH> to find it.
220
221=item args => [...]
222
223Arguments to pass to F<mpv>. These arguments are passed after the
224hardcoded arguments used by this module, but before the arguments passed
225ot C<start>. It does not matter whether you specify your arguments using
226this key, or in the C<start> call, but when you invoke F<mpv> multiple
227times, typically the arguments used for all invocations go here, while
228arguments used for specific invocations (e..g filenames) are passed to
229C<start>.
230
231=item trace => false|true|coderef
232
233Enables tracing if true. In trace mode, output from F<mpv> is printed to
234standard error using a C<< mpv> >> prefix, and commands sent to F<mpv>
235are printed with a C<< >mpv >> prefix.
236
237If a code reference is passed, then instead of printing to standard
238errort, this coderef is invoked with a first arfgument being either
239C<< mpv> >> or C<< >mpv >>, and the second argument being a string to
240display. The default implementation simply does this:
241
242 sub {
243 warn "$_[0] $_[1]\n";
244 }
245
246=item on_eof => $coderef->($mpv)
247
248=item on_event => $coderef->($mpv, $event, $data)
249
250=item on_key => $coderef->($mpv, $string)
251
252These are invoked by the default method implementation of the same name -
253see below.
254
255=back
256
257=cut
33 258
34sub new { 259sub new {
35 my ($class, %kv) = @_; 260 my ($class, %kv) = @_;
36 261
37 bless { 262 bless {
38 mpv => "mpv", 263 mpv => "mpv",
39 args => [], 264 args => [],
40 %kv, 265 %kv,
41 }, $class 266 }, $class
42} 267}
268
269=item $string = $mpv->escape_binary ($string)
270
271This module excects all command data sent to F<mpv> to be in unicode. Some
272things are not, such as filenames. To pass binary data such as filenames
273through a comamnd, you need to escape it using this method.
274
275The simplest example is a C<loadfile> command:
276
277 $mpv->cmd_recv (loadfile => $mpv->escape_binary ($path));
278
279=cut
43 280
44# can be used to escape filenames 281# can be used to escape filenames
45sub escape_binary { 282sub escape_binary {
46 shift; 283 shift;
47 local $_ = shift; 284 local $_ = shift;
48 # we escape every "illegal" octet using U+10e5df HEX. this is later undone in cmd 285 # 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; 286 s/([\x00-\x1f\x80-\xff])/sprintf "\x{10e5df}%02x", ord $1/ge;
50 $_ 287 $_
51} 288}
52 289
290=item $started = $mpv->start (argument...)
291
292Starts F<mpv>, passing the given arguemnts as extra arguments to
293F<mpv>. If F<mpv> is already running, it returns false, otherwise it
294returns a true value, so you can easily start F<mpv> on demand by calling
295C<start> just before using it, and if it is already running, it will not
296be started again.
297
298The arguments passwd to F<mpv> are a set of hardcoded built-in arguments,
299followed by the arguments specified in the constructor, followed by the
300arguments passwd to this method. The built-in arguments currently are
301F<--no-input-terminal>, F<--really-quiet> (or F<--quiet> in C<trace>
302mode), and C<--input-ipc-client> (or equivalent).
303
304Some commonly used and/or even useful arguments you might want to pass are:
305
306=over
307
308=item F<--idle=yes> or F<--idle=once> to keep F<mpv> from quitting when you
309don't specify a file to play.
310
311=item F<--pause>, to keep F<mpv> from instantly starting to play a file, in case you want to
312inspect/change properties first.
313
314=item F<--force-window=no> (or similar), to keep F<mpv> from instantly opening a window, or to force it to do so.
315
316=item F<--audio-client-name=yourappname>, to make sure audio streams are associated witht eh right program.
317
318=item F<--wid=id>, to embed F<mpv> into another application.
319
320=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.
321
322=back
323
324The return value can be used to decide whether F<mpv> needs initializing:
325
326 if ($mpv->start) {
327 $mpv->bind_key (...);
328 $mpv->cmd (set => property => value);
329 ...
330 }
331
332You can immediately starting sending commands when this method returns,
333even if F<mpv> has not yet started.
334
335=cut
336
53sub start { 337sub start {
54 my ($self, @extra_args) = @_; 338 my ($self, @extra_args) = @_;
55 339
56 return 0 if $self->{fh}; 340 return 0 if $self->{fh};
57 341
73 fcntl $slave, Fcntl::F_SETFD, 0; 357 fcntl $slave, Fcntl::F_SETFD, 0;
74 358
75 my $input_file = $options =~ /\s--input-ipc-client\s/ ? "input-ipc-client" : "input-file"; 359 my $input_file = $options =~ /\s--input-ipc-client\s/ ? "input-ipc-client" : "input-file";
76 360
77 exec $self->{mpv}, 361 exec $self->{mpv},
78 qw(--no-input-terminal --idle=yes --pause), 362 qw(--no-input-terminal),
79 ($self->{trace} ? "--quiet" : "--really-quiet"), 363 ($self->{trace} ? "--quiet" : "--really-quiet"),
80 "--$input_file=fd://" . (fileno $slave), 364 "--$input_file=fd://" . (fileno $slave),
81 @{ $self->{args} }, 365 @{ $self->{args} },
82 @extra_args; 366 @extra_args;
83 exit 1; 367 exit 1;
99 while ($buf =~ s/^([^\n]+)\n//) { 383 while ($buf =~ s/^([^\n]+)\n//) {
100 $trace->("mpv>" => "$1"); 384 $trace->("mpv>" => "$1");
101 385
102 if ("{" eq substr $1, 0, 1) { 386 if ("{" eq substr $1, 0, 1) {
103 eval { 387 eval {
104 my $reply = JSON::XS->new->latin1->decode ($1); 388 my $reply = $JSON->new->latin1->decode ($1);
105 389
106 if (exists $reply->{event}) { 390 if (exists $reply->{event}) {
107 if ( 391 if (
108 $reply->{event} eq "client-message" 392 $reply->{event} eq "client-message"
109 and $reply->{args}[0] eq "AnyEvent::MPV" 393 and $reply->{args}[0] eq "AnyEvent::MPV"
111 if ($reply->{args}[1] eq "key") { 395 if ($reply->{args}[1] eq "key") {
112 (my $key = $reply->{args}[2]) =~ s/\\x(..)/chr hex $1/ge; 396 (my $key = $reply->{args}[2]) =~ s/\\x(..)/chr hex $1/ge;
113 $self->on_key ($key); 397 $self->on_key ($key);
114 } 398 }
115 } else { 399 } else {
116 $self->on_event ($reply); 400 $self->on_event ($reply->{event}, $reply);
117 } 401 }
118 } elsif (exists $reply->{request_id}) { 402 } elsif (exists $reply->{request_id}) {
119 my $cv = delete $self->{cmd_cv}{$reply->{request_id}}; 403 my $cv = delete $self->{cmd_cv}{$reply->{request_id}};
120 404
121 unless ($cv) { 405 unless ($cv) {
159 }; 443 };
160 444
161 1 445 1
162} 446}
163 447
448=item $mpv->stop
449
450Ensures that F<mpv> is being stopped, by killing F<mpv> with a C<TERM>
451signal if needed. After this, you can C<< ->start >> a new instance again.
452
453=cut
454
455sub stop {
456 my ($self) = @_;
457
458 delete $self->{rw};
459 delete $self->{ww};
460
461 if ($self->{pid}) {
462
463 close delete $self->{fh}; # current mpv versions should cleanup on their own on close
464
465 kill TERM => $self->{pid};
466
467 }
468
469 delete $self->{pid};
470 delete $self->{cmd_cv};
471}
472
473=item $mpv->on_eof
474
475This method is called when F<mpv> quits - usually unexpectedly. The
476default implementation will call the C<on_eof> code reference specified in
477the constructor, or do nothing if none was given.
478
479For subclassing, see I<SUBCLASSING>, below.
480
481=cut
482
164sub on_eof { 483sub on_eof {
165 my ($self) = @_; 484 my ($self) = @_;
166 485
167 $self->{on_eof}($self) if $self->{on_eof}; 486 $self->{on_eof}($self) if $self->{on_eof};
168} 487}
169 488
489=item $mpv->on_event ($event, $data)
490
491This method is called when F<mpv> sends an asynchronous event. The default
492implementation will call the C<on_event> code reference specified in the
493constructor, or do nothing if none was given.
494
495The first/implicit argument is the C<$mpv> object, the second is the event
496name (same as C<< $data->{event} >>, purely for convenience), and the
497third argument is the full event object as sent by F<mpv>. See L<List of
498events|https://mpv.io/manual/stable/#list-of-events> in its documentation.
499
500For subclassing, see I<SUBCLASSING>, below.
501
502=cut
503
170sub on_event { 504sub on_event {
171 my ($self, $key) = @_; 505 my ($self, $key) = @_;
172 506
173 $self->{on_event}($self, $key) if $self->{on_event}; 507 $self->{on_event}($self, $key) if $self->{on_event};
174} 508}
175 509
510=item $mpv->on_key ($string)
511
512Invoked when a key declared by C<< ->bind_key >> is pressed. The default
513invokes the C<on_key> code reference specified in the constructor with the
514C<$mpv> object and the key name as arguments, or do nothing if none was
515given.
516
517For more details and examples, see the C<bind_key> method.
518
519For subclassing, see I<SUBCLASSING>, below.
520
521=cut
522
176sub on_key { 523sub on_key {
177 my ($self, $key) = @_; 524 my ($self, $key) = @_;
178 525
179 $self->{on_key}($self, $key) if $self->{on_key}; 526 $self->{on_key}($self, $key) if $self->{on_key};
180} 527}
181 528
529=item $mpv->cmd ($command => $arg, $arg...)
530
531Queues a command to be sent to F<mpv>, using the given arguments, and
532immediately return a condvar.
533
534See L<the mpv
535documentation|https://mpv.io/manual/stable/#list-of-input-commands> for
536details on individual commands.
537
538The condvar can be ignored:
539
540 $mpv->cmd (set_property => "deinterlace", "yes");
541
542Or it can be used to synchronously wait for the command results:
543
544 $cv = $mpv->cmd (get_property => "video-format");
545 $format = $cv->recv;
546
547 # or simpler:
548
549 $format = $mpv->cmd (get_property => "video-format")->recv;
550
551 # or even simpler:
552
553 $format = $mpv->cmd_recv (get_property => "video-format");
554
555Or you can set a callback:
556
557 $cv = $mpv->cmd (get_property => "video-format");
558 $cv->cb (sub {
559 my $format = $_[0]->recv;
560 });
561
562On error, the condvar will croak when C<recv> is called.
563
564=cut
565
182sub cmd { 566sub cmd {
183 my ($self, @cmd) = @_; 567 my ($self, @cmd) = @_;
184 568
185 my $cv = AE::cv; 569 my $cv = AE::cv;
186 570
187 my $reqid = ++$self->{reqid}; 571 my $reqid = ++$self->{reqid};
188 $self->{cmd_cv}{$reqid} = $cv; 572 $self->{cmd_cv}{$reqid} = $cv;
189 573
190 my $cmd = JSON::XS::encode_json { command => ref $cmd[0] ? $cmd[0] : \@cmd, request_id => $reqid*1 }; 574 my $cmd = $JSON->new->utf8->encode ({ command => ref $cmd[0] ? $cmd[0] : \@cmd, request_id => $reqid*1 });
191 575
192 # (un-)apply escape_binary hack 576 # (un-)apply escape_binary hack
193 $cmd =~ s/\xf4\x8e\x97\x9f(..)/sprintf sprintf "\\x%02x", hex $1/ges; # f48e979f == 10e5df in utf-8 577 $cmd =~ s/\xf4\x8e\x97\x9f(..)/sprintf sprintf "\\x%02x", hex $1/ges; # f48e979f == 10e5df in utf-8
194 578
195 $self->{_send}($cmd); 579 $self->{_send}($cmd);
196 580
197 $cv 581 $cv
198} 582}
199 583
584=item $result = $mpv->cmd_recv ($command => $arg, $arg...)
585
586The same as calling C<cmd> and immediately C<recv> on its return
587value. Useful when you don't want to mess with F<mpv> asynchronously or
588simply needs to have the result:
589
590 $mpv->cmd_recv ("stop");
591 $position = $mpv->cmd_recv ("get_property", "playback-time");
592
593=cut
594
200sub cmd_recv { 595sub cmd_recv {
201 &cmd->recv 596 &cmd->recv
202} 597}
203 598
599=item $mpv->bind_key ($INPUT => $string)
600
601This is an extension implement by this module to make it easy to get key events. The way this is implemented
602is to bind a C<client-message> witha first argument of C<AnyEvent::MPV> and the C<$string> you passed. This C<$string> is then
603passed ot the C<on_key> handle when the key is proessed, e.g.:
604
605 my $mpv = AnyEvent::MPV->new (
606 on_key => sub {
607 my ($mpv, $key) = @_;
608
609 if ($key eq "letmeout") {
610 print "user pressed escape\n";
611 }
612 },
613 );
614
615 $mpv_>bind_key (ESC => "letmeout");
616
617The key configuration is lost when F<mpv> is stopped and must be (re-)done
618after every C<start>.
619
620=cut
621
204sub bind_key { 622sub bind_key {
205 my ($self, $key, $event) = @_; 623 my ($self, $key, $event) = @_;
206 624
207 $event =~ s/([^A-Za-z0-9\-_])/sprintf "\\x%02x", ord $1/ge; 625 $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"); 626 $self->cmd (keybind => $key => "no-osd script-message AnyEvent::MPV key $event");
209} 627}
210 628
211sub stop { 629=back
212 my ($self) = @_;
213 630
214 if ($self->{pid}) { 631=head2 SUBCLASSING
215 delete $self->{rw};
216 delete $self->{ww};
217 632
218 close delete $self->{fh}; # current mpv versions should cleanup on their own on close 633Like most perl objects, C<AnyEvent::MPV> objects are implemented as
634hashes, with the constructor simply storing all passed key-value pairs in
635the object. If you want to subclass to provide your own C<on_*> methods,
636be my guest and rummage around in the internals as much as you wish - the
637only guarantee that this module dcoes is that it will not use keys with
638double colons in the name, so youc an use those, or chose to simply not
639care and deal with the breakage.
219 640
220 kill TERM => $self->{pid}; 641If you don't want to go to the effort of subclassing this module, you can
221 642also specify all event handlers as constructor keys.
222 delete $self->{pid};
223 }
224}
225 643
226=head1 SEE ALSO 644=head1 SEE ALSO
227 645
228L<AnyEvent>. 646L<AnyEvent>, L<the mpv command documentation|https://mpv.io/manual/stable/#command-interface>.
229 647
230=head1 AUTHOR 648=head1 AUTHOR
231 649
232 Marc Lehmann <schmorp@schmorp.de> 650 Marc Lehmann <schmorp@schmorp.de>
233 http://home.schmorp.de/ 651 http://home.schmorp.de/

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines