ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MPV/MPV.pm
Revision: 1.5
Committed: Sun Mar 19 23:13:25 2023 UTC (13 months, 4 weeks ago) by root
Branch: MAIN
Changes since 1.4: +436 -18 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 =head1 NAME
2
3 AnyEvent::MPV - remote control mpv (https://mpv.io)
4
5 =head1 SYNOPSIS
6
7 use AnyEvent::MPV;
8
9 =head1 DESCRIPTION
10
11 This module allows you to remote control F<mpv> (a video player). It also
12 is an L<AnyEvent> user, you need to make sure that you use and run a
13 supported event loop.
14
15 There are other modules doing this, and I haven't looked much at them
16 other than to decide that they don't handle encodings correctly, and since
17 none of them use AnyEvent, I wrote my own. When in doubt, have a look at
18 them, too.
19
20 Knowledge of the L<mpv command
21 interface|https://mpv.io/manual/stable/#command-interface> is required to
22 use this module.
23
24 Features 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
42 This module forks an F<mpv> process and uses F<--input-ipc-client> (or
43 equivalent) to create a bidirectional communication channel between it and
44 the F<mpv> process.
45
46 It then speaks the somewhat JSON-looking (but not really being JSON)
47 protocol that F<mpv> implements to both send it commands, decode and
48 handle replies, and handle asynchronous events.
49
50 Here 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
64 This starts F<mpv> with the two arguments C<--> and C<$videofile>, which
65 it should load and play. It then waits two seconds by starting a timer and
66 quits. The C<trace> argument to the constructor makes F<mpv> more verbose
67 and also prints the commands and responses, so you cna have an idea what
68 is going on.
69
70 This is not usually very useful (you could just run F<mpv> as a simple
71 shell 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
90 This specifies extra arguments in the constructor - these arguments are
91 used 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
93 F<--pause> keeps F<mpv> in pause mode (i.e. it does not play the file
94 after loading it), and C<--idle=yes> tells F<mpv> to not quit when it does
95 not have a playlist - as no files are specified on the command line.
96
97 To load a file, we then send it a C<loadfile> command, which accepts, as
98 first argument, the URL or path to a video file. To make sure F<mpv> does
99 not misinterpret the path as a URL, it was prefixed with F<./> (similarly
100 to "protecting" paths in perls C<open>).
101
102 Since commands send I<to> F<mpv> are send in UTF-8, we need to escape the
103 filename (which might be in any encoding) using the C<esscape_binary>
104 method - this is not needed if your filenames are just ascii, or magically
105 get interpreted correctly, but if you accept arbitrary filenamews (e.g.
106 from the user), you need to do this.
107
108 The C<cmd_recv> method then queues the command, waits for a reply and
109 returns the reply data (or croaks on error). F<mpv> would, at this point,
110 load the file and, if everything was successful, show the first frame and
111 pause. Note that, since F<mpv> is implement rather synchronously itself,
112 do not expect commands to fail in many circumstances - for example, fit
113 he file does not exit, you will likely get an event, but the C<loadfile>
114 command itself will run successfully.
115
116 To unpause, we send another command, C<set>, to set the C<pause> property
117 to C<no>, this time using the C<cmd> method, which queues the command, but
118 instead of waiting for a reply, it immediately returns a condvar that cna
119 be used to receive results.
120
121 This should then cause F<mpv> to start playing the video.
122
123 It then again waits two seconds and quits.
124
125 Now, just waiting two seconds is rather, eh, unuseful, so let's look at
126 receiving 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
155 This example uses a global condvar C<$quit> to wait for the file to finish
156 playing. Also, most of the logic is now in an C<on_event> callback, which
157 receives an event name and the actual event object.
158
159 The two events we handle are C<start-file>, which is emitted by F<mpv>
160 once it has loaded a new file, and C<end-file>, which signals the end
161 of a file.
162
163 In the former event, we again set the C<pause> property to C<no> so the
164 movie starts playing. For the latter event, we tell the main program to
165 quit by invoking C<$quit>.
166
167 This should conclude the basics of operation. There are a few more
168 examples later in the documentation.
169
170 =head2 ENCODING CONVENTIONS
171
172 As a rule of thumb, all data you pass to this module to be sent to F<mpv>
173 is expected to be in unicode. To pass something that isn't, you need to
174 escape it using C<escape_binary>.
175
176 Data received from C<$mpv>, however, is I<not> decoded to unicode, as data
177 returned by F<mpv> is not generally encoded in unicode, and the encoding
178 is usually unspecified. So if you receive data and expect it to be in
179 unicode, you need to first decode it from UTF-8, but note that this might
180 fail. This is not a limitation of this module - F<mpv> simply does not
181 specify nor guarantee a specific encoding, or any encoding at all, in its
182 protocol.
183
184 =head2 METHODS
185
186 =over
187
188 =cut
189
190 package AnyEvent::MPV;
191
192 use common::sense;
193
194 use Fcntl ();
195 use Scalar::Util ();
196
197 use AnyEvent ();
198 use AnyEvent::Util ();
199
200 our $JSON = eval { require JSON::XS; JSON::XS:: }
201 || do { require JSON::PP; JSON::PP:: };
202
203 our $JSON_CODER =
204
205 our $VERSION = '0.1';
206
207 our $mpv_path; # last mpv path used
208 our $mpv_optionlist; # output of mpv --list-options
209
210 =item $mpv = AnyEvent::MPV->new (key => value...)
211
212 Creates 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
218 The path to the F<mpv> binary to use - by default, C<mpv> is used and
219 therefore, uses your C<PATH> to find it.
220
221 =item args => [...]
222
223 Arguments to pass to F<mpv>. These arguments are passed after the
224 hardcoded arguments used by this module, but before the arguments passed
225 ot C<start>. It does not matter whether you specify your arguments using
226 this key, or in the C<start> call, but when you invoke F<mpv> multiple
227 times, typically the arguments used for all invocations go here, while
228 arguments used for specific invocations (e..g filenames) are passed to
229 C<start>.
230
231 =item trace => false|true|coderef
232
233 Enables tracing if true. In trace mode, output from F<mpv> is printed to
234 standard error using a C<< mpv> >> prefix, and commands sent to F<mpv>
235 are printed with a C<< >mpv >> prefix.
236
237 If a code reference is passed, then instead of printing to standard
238 errort, this coderef is invoked with a first arfgument being either
239 C<< mpv> >> or C<< >mpv >>, and the second argument being a string to
240 display. 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
252 These are invoked by the default method implementation of the same name -
253 see below.
254
255 =back
256
257 =cut
258
259 sub new {
260 my ($class, %kv) = @_;
261
262 bless {
263 mpv => "mpv",
264 args => [],
265 %kv,
266 }, $class
267 }
268
269 =item $string = $mpv->escape_binary ($string)
270
271 This module excects all command data sent to F<mpv> to be in unicode. Some
272 things are not, such as filenames. To pass binary data such as filenames
273 through a comamnd, you need to escape it using this method.
274
275 The simplest example is a C<loadfile> command:
276
277 $mpv->cmd_recv (loadfile => $mpv->escape_binary ($path));
278
279 =cut
280
281 # can be used to escape filenames
282 sub escape_binary {
283 shift;
284 local $_ = shift;
285 # we escape every "illegal" octet using U+10e5df HEX. this is later undone in cmd
286 s/([\x00-\x1f\x80-\xff])/sprintf "\x{10e5df}%02x", ord $1/ge;
287 $_
288 }
289
290 =item $started = $mpv->start (argument...)
291
292 Starts F<mpv>, passing the given arguemnts as extra arguments to
293 F<mpv>. If F<mpv> is already running, it returns false, otherwise it
294 returns a true value, so you can easily start F<mpv> on demand by calling
295 C<start> just before using it, and if it is already running, it will not
296 be started again.
297
298 The arguments passwd to F<mpv> are a set of hardcoded built-in arguments,
299 followed by the arguments specified in the constructor, followed by the
300 arguments passwd to this method. The built-in arguments currently are
301 F<--no-input-terminal>, F<--really-quiet> (or F<--quiet> in C<trace>
302 mode), and C<--input-ipc-client> (or equivalent).
303
304 Some 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
309 don'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
312 inspect/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
324 The 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
332 You can immediately starting sending commands when this method returns,
333 even if F<mpv> has not yet started.
334
335 =cut
336
337 sub start {
338 my ($self, @extra_args) = @_;
339
340 return 0 if $self->{fh};
341
342 # cache optionlist for same "path"
343 ($mpv_path, $mpv_optionlist) = ($self->{mpv}, scalar qx{\Q$self->{mpv}\E --list-options})
344 if $self->{mpv} ne $mpv_path;
345
346 my $options = $mpv_optionlist;
347
348 my ($fh, $slave) = AnyEvent::Util::portable_socketpair
349 or die "socketpair: $!\n";
350
351 AnyEvent::Util::fh_nonblocking $fh, 1;
352
353 $self->{pid} = fork;
354
355 if ($self->{pid} eq 0) {
356 AnyEvent::Util::fh_nonblocking $slave, 0;
357 fcntl $slave, Fcntl::F_SETFD, 0;
358
359 my $input_file = $options =~ /\s--input-ipc-client\s/ ? "input-ipc-client" : "input-file";
360
361 exec $self->{mpv},
362 qw(--no-input-terminal),
363 ($self->{trace} ? "--quiet" : "--really-quiet"),
364 "--$input_file=fd://" . (fileno $slave),
365 @{ $self->{args} },
366 @extra_args;
367 exit 1;
368 }
369
370 $self->{fh} = $fh;
371
372 my $trace = delete $self->{trace} || sub { };
373
374 $trace = sub { warn "$_[0] $_[1]\n" } if $trace && !ref $trace;
375
376 my $buf;
377 my $wbuf;
378
379 Scalar::Util::weaken $self;
380
381 $self->{rw} = AE::io $fh, 0, sub {
382 if (sysread $fh, $buf, 8192, length $buf) {
383 while ($buf =~ s/^([^\n]+)\n//) {
384 $trace->("mpv>" => "$1");
385
386 if ("{" eq substr $1, 0, 1) {
387 eval {
388 my $reply = $JSON->new->latin1->decode ($1);
389
390 if (exists $reply->{event}) {
391 if (
392 $reply->{event} eq "client-message"
393 and $reply->{args}[0] eq "AnyEvent::MPV"
394 ) {
395 if ($reply->{args}[1] eq "key") {
396 (my $key = $reply->{args}[2]) =~ s/\\x(..)/chr hex $1/ge;
397 $self->on_key ($key);
398 }
399 } else {
400 $self->on_event ($reply->{event}, $reply);
401 }
402 } elsif (exists $reply->{request_id}) {
403 my $cv = delete $self->{cmd_cv}{$reply->{request_id}};
404
405 unless ($cv) {
406 warn "no cv found for request id <$reply->{request_id}>\n";
407 next;
408 }
409
410 if (exists $reply->{data}) {
411 $cv->send ($reply->{data});
412 } elsif ($reply->{error} eq "success") { # success means error... eh.. no...
413 $cv->send;
414 } else {
415 $cv->croak ($reply->{error});
416 }
417
418 } else {
419 warn "unexpected reply from mpv, pleasew report: <$1>\n";
420 }
421 };
422 warn $@ if $@;
423 } else {
424 $trace->("mpv>" => "$1");
425 }
426 }
427 } else {
428 $self->stop;
429 $self->on_eof;
430 }
431 };
432
433 $self->{_send} = sub {
434 $wbuf .= "$_[0]\n";
435
436 $trace->(">mpv" => "$_[0]");
437
438 $self->{ww} ||= AE::io $fh, 1, sub {
439 my $len = syswrite $fh, $wbuf;
440 substr $wbuf, 0, $len, "";
441 undef $self->{ww} unless length $wbuf;
442 };
443 };
444
445 1
446 }
447
448 =item $mpv->stop
449
450 Ensures that F<mpv> is being stopped, by killing F<mpv> with a C<TERM>
451 signal if needed. After this, you can C<< ->start >> a new instance again.
452
453 =cut
454
455 sub 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
475 This method is called when F<mpv> quits - usually unexpectedly. The
476 default implementation will call the C<on_eof> code reference specified in
477 the constructor, or do nothing if none was given.
478
479 For subclassing, see I<SUBCLASSING>, below.
480
481 =cut
482
483 sub on_eof {
484 my ($self) = @_;
485
486 $self->{on_eof}($self) if $self->{on_eof};
487 }
488
489 =item $mpv->on_event ($event, $data)
490
491 This method is called when F<mpv> sends an asynchronous event. The default
492 implementation will call the C<on_event> code reference specified in the
493 constructor, or do nothing if none was given.
494
495 The first/implicit argument is the C<$mpv> object, the second is the event
496 name (same as C<< $data->{event} >>, purely for convenience), and the
497 third argument is the full event object as sent by F<mpv>. See L<List of
498 events|https://mpv.io/manual/stable/#list-of-events> in its documentation.
499
500 For subclassing, see I<SUBCLASSING>, below.
501
502 =cut
503
504 sub on_event {
505 my ($self, $key) = @_;
506
507 $self->{on_event}($self, $key) if $self->{on_event};
508 }
509
510 =item $mpv->on_key ($string)
511
512 Invoked when a key declared by C<< ->bind_key >> is pressed. The default
513 invokes the C<on_key> code reference specified in the constructor with the
514 C<$mpv> object and the key name as arguments, or do nothing if none was
515 given.
516
517 For more details and examples, see the C<bind_key> method.
518
519 For subclassing, see I<SUBCLASSING>, below.
520
521 =cut
522
523 sub on_key {
524 my ($self, $key) = @_;
525
526 $self->{on_key}($self, $key) if $self->{on_key};
527 }
528
529 =item $mpv->cmd ($command => $arg, $arg...)
530
531 Queues a command to be sent to F<mpv>, using the given arguments, and
532 immediately return a condvar.
533
534 See L<the mpv
535 documentation|https://mpv.io/manual/stable/#list-of-input-commands> for
536 details on individual commands.
537
538 The condvar can be ignored:
539
540 $mpv->cmd (set_property => "deinterlace", "yes");
541
542 Or 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
555 Or 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
562 On error, the condvar will croak when C<recv> is called.
563
564 =cut
565
566 sub cmd {
567 my ($self, @cmd) = @_;
568
569 my $cv = AE::cv;
570
571 my $reqid = ++$self->{reqid};
572 $self->{cmd_cv}{$reqid} = $cv;
573
574 my $cmd = $JSON->new->utf8->encode ({ command => ref $cmd[0] ? $cmd[0] : \@cmd, request_id => $reqid*1 });
575
576 # (un-)apply escape_binary hack
577 $cmd =~ s/\xf4\x8e\x97\x9f(..)/sprintf sprintf "\\x%02x", hex $1/ges; # f48e979f == 10e5df in utf-8
578
579 $self->{_send}($cmd);
580
581 $cv
582 }
583
584 =item $result = $mpv->cmd_recv ($command => $arg, $arg...)
585
586 The same as calling C<cmd> and immediately C<recv> on its return
587 value. Useful when you don't want to mess with F<mpv> asynchronously or
588 simply needs to have the result:
589
590 $mpv->cmd_recv ("stop");
591 $position = $mpv->cmd_recv ("get_property", "playback-time");
592
593 =cut
594
595 sub cmd_recv {
596 &cmd->recv
597 }
598
599 =item $mpv->bind_key ($INPUT => $string)
600
601 This is an extension implement by this module to make it easy to get key events. The way this is implemented
602 is to bind a C<client-message> witha first argument of C<AnyEvent::MPV> and the C<$string> you passed. This C<$string> is then
603 passed 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
617 The key configuration is lost when F<mpv> is stopped and must be (re-)done
618 after every C<start>.
619
620 =cut
621
622 sub bind_key {
623 my ($self, $key, $event) = @_;
624
625 $event =~ s/([^A-Za-z0-9\-_])/sprintf "\\x%02x", ord $1/ge;
626 $self->cmd (keybind => $key => "no-osd script-message AnyEvent::MPV key $event");
627 }
628
629 =back
630
631 =head2 SUBCLASSING
632
633 Like most perl objects, C<AnyEvent::MPV> objects are implemented as
634 hashes, with the constructor simply storing all passed key-value pairs in
635 the object. If you want to subclass to provide your own C<on_*> methods,
636 be my guest and rummage around in the internals as much as you wish - the
637 only guarantee that this module dcoes is that it will not use keys with
638 double colons in the name, so youc an use those, or chose to simply not
639 care and deal with the breakage.
640
641 If you don't want to go to the effort of subclassing this module, you can
642 also specify all event handlers as constructor keys.
643
644 =head1 SEE ALSO
645
646 L<AnyEvent>, L<the mpv command documentation|https://mpv.io/manual/stable/#command-interface>.
647
648 =head1 AUTHOR
649
650 Marc Lehmann <schmorp@schmorp.de>
651 http://home.schmorp.de/
652
653 =cut
654
655 1
656