ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MPV/MPV.pm
Revision: 1.7
Committed: Sun Mar 19 23:37:46 2023 UTC (13 months, 4 weeks ago) by root
Branch: MAIN
Changes since 1.6: +6 -5 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 can have an idea what
68 is going on.
69
70 In 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
84 This is not usually very useful (you could just run F<mpv> as a simple
85 shell 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
104 This specifies extra arguments in the constructor - these arguments are
105 used 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
107 F<--pause> keeps F<mpv> in pause mode (i.e. it does not play the file
108 after loading it), and C<--idle=yes> tells F<mpv> to not quit when it does
109 not have a playlist - as no files are specified on the command line.
110
111 To load a file, we then send it a C<loadfile> command, which accepts, as
112 first argument, the URL or path to a video file. To make sure F<mpv> does
113 not misinterpret the path as a URL, it was prefixed with F<./> (similarly
114 to "protecting" paths in perls C<open>).
115
116 Since commands send I<to> F<mpv> are send in UTF-8, we need to escape the
117 filename (which might be in any encoding) using the C<esscape_binary>
118 method - this is not needed if your filenames are just ascii, or magically
119 get interpreted correctly, but if you accept arbitrary filenamews (e.g.
120 from the user), you need to do this.
121
122 The C<cmd_recv> method then queues the command, waits for a reply and
123 returns the reply data (or croaks on error). F<mpv> would, at this point,
124 load the file and, if everything was successful, show the first frame and
125 pause. Note that, since F<mpv> is implement rather synchronously itself,
126 do not expect commands to fail in many circumstances - for example, fit
127 he file does not exit, you will likely get an event, but the C<loadfile>
128 command itself will run successfully.
129
130 To unpause, we send another command, C<set>, to set the C<pause> property
131 to C<no>, this time using the C<cmd> method, which queues the command, but
132 instead of waiting for a reply, it immediately returns a condvar that cna
133 be used to receive results.
134
135 This should then cause F<mpv> to start playing the video.
136
137 It then again waits two seconds and quits.
138
139 Now, just waiting two seconds is rather, eh, unuseful, so let's look at
140 receiving 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
169 This example uses a global condvar C<$quit> to wait for the file to finish
170 playing. Also, most of the logic is now in an C<on_event> callback, which
171 receives an event name and the actual event object.
172
173 The two events we handle are C<start-file>, which is emitted by F<mpv>
174 once it has loaded a new file, and C<end-file>, which signals the end
175 of a file.
176
177 In the former event, we again set the C<pause> property to C<no> so the
178 movie starts playing. For the latter event, we tell the main program to
179 quit by invoking C<$quit>.
180
181 This should conclude the basics of operation. There are a few more
182 examples later in the documentation.
183
184 =head2 ENCODING CONVENTIONS
185
186 As a rule of thumb, all data you pass to this module to be sent to F<mpv>
187 is expected to be in unicode. To pass something that isn't, you need to
188 escape it using C<escape_binary>.
189
190 Data received from C<$mpv>, however, is I<not> decoded to unicode, as data
191 returned by F<mpv> is not generally encoded in unicode, and the encoding
192 is usually unspecified. So if you receive data and expect it to be in
193 unicode, you need to first decode it from UTF-8, but note that this might
194 fail. This is not a limitation of this module - F<mpv> simply does not
195 specify nor guarantee a specific encoding, or any encoding at all, in its
196 protocol.
197
198 =head2 METHODS
199
200 =over
201
202 =cut
203
204 package AnyEvent::MPV;
205
206 use common::sense;
207
208 use Fcntl ();
209 use Scalar::Util ();
210
211 use AnyEvent ();
212 use AnyEvent::Util ();
213
214 our $JSON = eval { require JSON::XS; JSON::XS:: }
215 || do { require JSON::PP; JSON::PP:: };
216
217 our $JSON_CODER =
218
219 our $VERSION = '0.1';
220
221 our $mpv_path; # last mpv path used
222 our $mpv_optionlist; # output of mpv --list-options
223
224 =item $mpv = AnyEvent::MPV->new (key => value...)
225
226 Creates 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
232 The path to the F<mpv> binary to use - by default, C<mpv> is used and
233 therefore, uses your C<PATH> to find it.
234
235 =item args => [...]
236
237 Arguments to pass to F<mpv>. These arguments are passed after the
238 hardcoded arguments used by this module, but before the arguments passed
239 ot C<start>. It does not matter whether you specify your arguments using
240 this key, or in the C<start> call, but when you invoke F<mpv> multiple
241 times, typically the arguments used for all invocations go here, while
242 arguments used for specific invocations (e..g filenames) are passed to
243 C<start>.
244
245 =item trace => false|true|coderef
246
247 Enables tracing if true. In trace mode, output from F<mpv> is printed to
248 standard error using a C<< mpv> >> prefix, and commands sent to F<mpv>
249 are printed with a C<< >mpv >> prefix.
250
251 If a code reference is passed, then instead of printing to standard
252 errort, this coderef is invoked with a first arfgument being either
253 C<< mpv> >> or C<< >mpv >>, and the second argument being a string to
254 display. 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
266 These are invoked by the default method implementation of the same name -
267 see below.
268
269 =back
270
271 =cut
272
273 sub new {
274 my ($class, %kv) = @_;
275
276 bless {
277 mpv => "mpv",
278 args => [],
279 %kv,
280 }, $class
281 }
282
283 =item $string = $mpv->escape_binary ($string)
284
285 This module excects all command data sent to F<mpv> to be in unicode. Some
286 things are not, such as filenames. To pass binary data such as filenames
287 through a comamnd, you need to escape it using this method.
288
289 The simplest example is a C<loadfile> command:
290
291 $mpv->cmd_recv (loadfile => $mpv->escape_binary ($path));
292
293 =cut
294
295 # can be used to escape filenames
296 sub escape_binary {
297 shift;
298 local $_ = shift;
299 # we escape every "illegal" octet using U+10e5df HEX. this is later undone in cmd
300 s/([\x00-\x1f\x80-\xff])/sprintf "\x{10e5df}%02x", ord $1/ge;
301 $_
302 }
303
304 =item $started = $mpv->start (argument...)
305
306 Starts F<mpv>, passing the given arguemnts as extra arguments to
307 F<mpv>. If F<mpv> is already running, it returns false, otherwise it
308 returns a true value, so you can easily start F<mpv> on demand by calling
309 C<start> just before using it, and if it is already running, it will not
310 be started again.
311
312 The arguments passwd to F<mpv> are a set of hardcoded built-in arguments,
313 followed by the arguments specified in the constructor, followed by the
314 arguments passwd to this method. The built-in arguments currently are
315 F<--no-input-terminal>, F<--really-quiet> (or F<--quiet> in C<trace>
316 mode), and C<--input-ipc-client> (or equivalent).
317
318 Some 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
323 don'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
326 inspect/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
338 The 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
346 You can immediately starting sending commands when this method returns,
347 even if F<mpv> has not yet started.
348
349 =cut
350
351 sub start {
352 my ($self, @extra_args) = @_;
353
354 return 0 if $self->{fh};
355
356 # cache optionlist for same "path"
357 ($mpv_path, $mpv_optionlist) = ($self->{mpv}, scalar qx{\Q$self->{mpv}\E --list-options})
358 if $self->{mpv} ne $mpv_path;
359
360 my $options = $mpv_optionlist;
361
362 my ($fh, $slave) = AnyEvent::Util::portable_socketpair
363 or die "socketpair: $!\n";
364
365 AnyEvent::Util::fh_nonblocking $fh, 1;
366
367 $self->{pid} = fork;
368
369 if ($self->{pid} eq 0) {
370 AnyEvent::Util::fh_nonblocking $slave, 0;
371 fcntl $slave, Fcntl::F_SETFD, 0;
372
373 my $input_file = $options =~ /\s--input-ipc-client\s/ ? "input-ipc-client" : "input-file";
374
375 exec $self->{mpv},
376 qw(--no-input-terminal),
377 ($self->{trace} ? "--quiet" : "--really-quiet"),
378 "--$input_file=fd://" . (fileno $slave),
379 @{ $self->{args} },
380 @extra_args;
381 exit 1;
382 }
383
384 $self->{fh} = $fh;
385
386 my $trace = delete $self->{trace} || sub { };
387
388 $trace = sub { warn "$_[0] $_[1]\n" } if $trace && !ref $trace;
389
390 my $buf;
391 my $wbuf;
392
393 Scalar::Util::weaken $self;
394
395 $self->{rw} = AE::io $fh, 0, sub {
396 if (sysread $fh, $buf, 8192, length $buf) {
397 while ($buf =~ s/^([^\n]+)\n//) {
398 $trace->("mpv>" => "$1");
399
400 if ("{" eq substr $1, 0, 1) {
401 eval {
402 my $reply = $JSON->new->latin1->decode ($1);
403
404 if (exists $reply->{event}) {
405 if (
406 $reply->{event} eq "client-message"
407 and $reply->{args}[0] eq "AnyEvent::MPV"
408 ) {
409 if ($reply->{args}[1] eq "key") {
410 (my $key = $reply->{args}[2]) =~ s/\\x(..)/chr hex $1/ge;
411 $self->on_key ($key);
412 }
413 } else {
414 $self->on_event (delete $reply->{event}, $reply);
415 }
416 } elsif (exists $reply->{request_id}) {
417 my $cv = delete $self->{cmd_cv}{$reply->{request_id}};
418
419 unless ($cv) {
420 warn "no cv found for request id <$reply->{request_id}>\n";
421 next;
422 }
423
424 if (exists $reply->{data}) {
425 $cv->send ($reply->{data});
426 } elsif ($reply->{error} eq "success") { # success means error... eh.. no...
427 $cv->send;
428 } else {
429 $cv->croak ($reply->{error});
430 }
431
432 } else {
433 warn "unexpected reply from mpv, pleasew report: <$1>\n";
434 }
435 };
436 warn $@ if $@;
437 } else {
438 $trace->("mpv>" => "$1");
439 }
440 }
441 } else {
442 $self->stop;
443 $self->on_eof;
444 }
445 };
446
447 $self->{_send} = sub {
448 $wbuf .= "$_[0]\n";
449
450 $trace->(">mpv" => "$_[0]");
451
452 $self->{ww} ||= AE::io $fh, 1, sub {
453 my $len = syswrite $fh, $wbuf;
454 substr $wbuf, 0, $len, "";
455 undef $self->{ww} unless length $wbuf;
456 };
457 };
458
459 1
460 }
461
462 =item $mpv->stop
463
464 Ensures that F<mpv> is being stopped, by killing F<mpv> with a C<TERM>
465 signal if needed. After this, you can C<< ->start >> a new instance again.
466
467 =cut
468
469 sub stop {
470 my ($self) = @_;
471
472 delete $self->{rw};
473 delete $self->{ww};
474
475 if ($self->{pid}) {
476
477 close delete $self->{fh}; # current mpv versions should cleanup on their own on close
478
479 kill TERM => $self->{pid};
480
481 }
482
483 delete $self->{pid};
484 delete $self->{cmd_cv};
485 }
486
487 =item $mpv->on_eof
488
489 This method is called when F<mpv> quits - usually unexpectedly. The
490 default implementation will call the C<on_eof> code reference specified in
491 the constructor, or do nothing if none was given.
492
493 For subclassing, see I<SUBCLASSING>, below.
494
495 =cut
496
497 sub on_eof {
498 my ($self) = @_;
499
500 $self->{on_eof}($self) if $self->{on_eof};
501 }
502
503 =item $mpv->on_event ($event, $data)
504
505 This method is called when F<mpv> sends an asynchronous event. The default
506 implementation will call the C<on_event> code reference specified in the
507 constructor, or do nothing if none was given.
508
509 The first/implicit argument is the C<$mpv> object, the second is the
510 event name (same as C<< $data->{event} >>, purely for convenience), and
511 the third argument is the event object as sent by F<mpv> (sans C<event>
512 key). See L<List of events|https://mpv.io/manual/stable/#list-of-events>
513 in its documentation.
514
515 For subclassing, see I<SUBCLASSING>, below.
516
517 =cut
518
519 sub on_event {
520 my ($self, $key) = @_;
521
522 $self->{on_event}($self, $key) if $self->{on_event};
523 }
524
525 =item $mpv->on_key ($string)
526
527 Invoked when a key declared by C<< ->bind_key >> is pressed. The default
528 invokes the C<on_key> code reference specified in the constructor with the
529 C<$mpv> object and the key name as arguments, or do nothing if none was
530 given.
531
532 For more details and examples, see the C<bind_key> method.
533
534 For subclassing, see I<SUBCLASSING>, below.
535
536 =cut
537
538 sub on_key {
539 my ($self, $key) = @_;
540
541 $self->{on_key}($self, $key) if $self->{on_key};
542 }
543
544 =item $mpv->cmd ($command => $arg, $arg...)
545
546 Queues a command to be sent to F<mpv>, using the given arguments, and
547 immediately return a condvar.
548
549 See L<the mpv
550 documentation|https://mpv.io/manual/stable/#list-of-input-commands> for
551 details on individual commands.
552
553 The condvar can be ignored:
554
555 $mpv->cmd (set_property => "deinterlace", "yes");
556
557 Or it can be used to synchronously wait for the command results:
558
559 $cv = $mpv->cmd (get_property => "video-format");
560 $format = $cv->recv;
561
562 # or simpler:
563
564 $format = $mpv->cmd (get_property => "video-format")->recv;
565
566 # or even simpler:
567
568 $format = $mpv->cmd_recv (get_property => "video-format");
569
570 Or you can set a callback:
571
572 $cv = $mpv->cmd (get_property => "video-format");
573 $cv->cb (sub {
574 my $format = $_[0]->recv;
575 });
576
577 On error, the condvar will croak when C<recv> is called.
578
579 =cut
580
581 sub cmd {
582 my ($self, @cmd) = @_;
583
584 my $cv = AE::cv;
585
586 my $reqid = ++$self->{reqid};
587 $self->{cmd_cv}{$reqid} = $cv;
588
589 my $cmd = $JSON->new->utf8->encode ({ command => ref $cmd[0] ? $cmd[0] : \@cmd, request_id => $reqid*1 });
590
591 # (un-)apply escape_binary hack
592 $cmd =~ s/\xf4\x8e\x97\x9f(..)/sprintf sprintf "\\x%02x", hex $1/ges; # f48e979f == 10e5df in utf-8
593
594 $self->{_send}($cmd);
595
596 $cv
597 }
598
599 =item $result = $mpv->cmd_recv ($command => $arg, $arg...)
600
601 The same as calling C<cmd> and immediately C<recv> on its return
602 value. Useful when you don't want to mess with F<mpv> asynchronously or
603 simply needs to have the result:
604
605 $mpv->cmd_recv ("stop");
606 $position = $mpv->cmd_recv ("get_property", "playback-time");
607
608 =cut
609
610 sub cmd_recv {
611 &cmd->recv
612 }
613
614 =item $mpv->bind_key ($INPUT => $string)
615
616 This is an extension implement by this module to make it easy to get key events. The way this is implemented
617 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
618 passed ot the C<on_key> handle when the key is proessed, e.g.:
619
620 my $mpv = AnyEvent::MPV->new (
621 on_key => sub {
622 my ($mpv, $key) = @_;
623
624 if ($key eq "letmeout") {
625 print "user pressed escape\n";
626 }
627 },
628 );
629
630 $mpv_>bind_key (ESC => "letmeout");
631
632 The key configuration is lost when F<mpv> is stopped and must be (re-)done
633 after every C<start>.
634
635 =cut
636
637 sub bind_key {
638 my ($self, $key, $event) = @_;
639
640 $event =~ s/([^A-Za-z0-9\-_])/sprintf "\\x%02x", ord $1/ge;
641 $self->cmd (keybind => $key => "no-osd script-message AnyEvent::MPV key $event");
642 }
643
644 =back
645
646 =head2 SUBCLASSING
647
648 Like most perl objects, C<AnyEvent::MPV> objects are implemented as
649 hashes, with the constructor simply storing all passed key-value pairs in
650 the object. If you want to subclass to provide your own C<on_*> methods,
651 be my guest and rummage around in the internals as much as you wish - the
652 only guarantee that this module dcoes is that it will not use keys with
653 double colons in the name, so youc an use those, or chose to simply not
654 care and deal with the breakage.
655
656 If you don't want to go to the effort of subclassing this module, you can
657 also specify all event handlers as constructor keys.
658
659 =head1 SEE ALSO
660
661 L<AnyEvent>, L<the mpv command documentation|https://mpv.io/manual/stable/#command-interface>.
662
663 =head1 AUTHOR
664
665 Marc Lehmann <schmorp@schmorp.de>
666 http://home.schmorp.de/
667
668 =cut
669
670 1
671