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

# User Rev Content
1 root 1.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 root 1.5 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 root 1.1
188     =cut
189    
190     package AnyEvent::MPV;
191    
192     use common::sense;
193    
194 root 1.2 use Fcntl ();
195     use Scalar::Util ();
196    
197 root 1.1 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 root 1.5 our $JSON_CODER =
204    
205 root 1.1 our $VERSION = '0.1';
206    
207     our $mpv_path; # last mpv path used
208     our $mpv_optionlist; # output of mpv --list-options
209    
210 root 1.5 =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 root 1.1 sub new {
260     my ($class, %kv) = @_;
261    
262     bless {
263     mpv => "mpv",
264     args => [],
265     %kv,
266     }, $class
267     }
268    
269 root 1.5 =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 root 1.1 # 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 root 1.5 =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 root 1.1 sub start {
338     my ($self, @extra_args) = @_;
339    
340 root 1.4 return 0 if $self->{fh};
341 root 1.1
342     # cache optionlist for same "path"
343 root 1.2 ($mpv_path, $mpv_optionlist) = ($self->{mpv}, scalar qx{\Q$self->{mpv}\E --list-options})
344 root 1.1 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 root 1.2 AnyEvent::Util::fh_nonblocking $fh, 1;
352 root 1.1
353 root 1.2 $self->{pid} = fork;
354 root 1.1
355     if ($self->{pid} eq 0) {
356 root 1.2 AnyEvent::Util::fh_nonblocking $slave, 0;
357     fcntl $slave, Fcntl::F_SETFD, 0;
358    
359 root 1.1 my $input_file = $options =~ /\s--input-ipc-client\s/ ? "input-ipc-client" : "input-file";
360    
361     exec $self->{mpv},
362 root 1.5 qw(--no-input-terminal),
363 root 1.1 ($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 root 1.2 my $trace = delete $self->{trace} || sub { };
373 root 1.1
374     $trace = sub { warn "$_[0] $_[1]\n" } if $trace && !ref $trace;
375    
376     my $buf;
377     my $wbuf;
378 root 1.2
379     Scalar::Util::weaken $self;
380 root 1.1
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 root 1.5 my $reply = $JSON->new->latin1->decode ($1);
389 root 1.1
390     if (exists $reply->{event}) {
391     if (
392     $reply->{event} eq "client-message"
393     and $reply->{args}[0] eq "AnyEvent::MPV"
394     ) {
395 root 1.3 if ($reply->{args}[1] eq "key") {
396 root 1.4 (my $key = $reply->{args}[2]) =~ s/\\x(..)/chr hex $1/ge;
397     $self->on_key ($key);
398 root 1.3 }
399 root 1.1 } else {
400 root 1.5 $self->on_event ($reply->{event}, $reply);
401 root 1.1 }
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 root 1.2 $self->stop;
429 root 1.1 $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 root 1.4
445     1
446 root 1.1 }
447    
448 root 1.5 =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 root 1.1 sub on_eof {
484     my ($self) = @_;
485    
486     $self->{on_eof}($self) if $self->{on_eof};
487     }
488    
489 root 1.5 =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 root 1.1 sub on_event {
505     my ($self, $key) = @_;
506    
507     $self->{on_event}($self, $key) if $self->{on_event};
508     }
509    
510 root 1.5 =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 root 1.2 sub on_key {
524 root 1.1 my ($self, $key) = @_;
525    
526 root 1.2 $self->{on_key}($self, $key) if $self->{on_key};
527 root 1.1 }
528    
529 root 1.5 =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 root 1.1 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 root 1.5 my $cmd = $JSON->new->utf8->encode ({ command => ref $cmd[0] ? $cmd[0] : \@cmd, request_id => $reqid*1 });
575 root 1.1
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 root 1.5 =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 root 1.4 sub cmd_recv {
596     &cmd->recv
597     }
598    
599 root 1.5 =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 root 1.4 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 root 1.5 =back
630 root 1.1
631 root 1.5 =head2 SUBCLASSING
632 root 1.1
633 root 1.5 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 root 1.1
641 root 1.5 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 root 1.1
644     =head1 SEE ALSO
645    
646 root 1.5 L<AnyEvent>, L<the mpv command documentation|https://mpv.io/manual/stable/#command-interface>.
647 root 1.1
648     =head1 AUTHOR
649    
650     Marc Lehmann <schmorp@schmorp.de>
651     http://home.schmorp.de/
652    
653     =cut
654    
655     1
656