ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MPV/MPV.pm
Revision: 1.6
Committed: Sun Mar 19 23:24:20 2023 UTC (13 months, 4 weeks ago) by root
Branch: MAIN
CVS Tags: rel-0_1
Changes since 1.5: +15 -1 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 root 1.6 and also prints the commands and responses, so you can have an idea what
68 root 1.5 is going on.
69    
70 root 1.6 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 root 1.5 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 root 1.1
202     =cut
203    
204     package AnyEvent::MPV;
205    
206     use common::sense;
207    
208 root 1.2 use Fcntl ();
209     use Scalar::Util ();
210    
211 root 1.1 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 root 1.5 our $JSON_CODER =
218    
219 root 1.1 our $VERSION = '0.1';
220    
221     our $mpv_path; # last mpv path used
222     our $mpv_optionlist; # output of mpv --list-options
223    
224 root 1.5 =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 root 1.1 sub new {
274     my ($class, %kv) = @_;
275    
276     bless {
277     mpv => "mpv",
278     args => [],
279     %kv,
280     }, $class
281     }
282    
283 root 1.5 =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 root 1.1 # 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 root 1.5 =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 root 1.1 sub start {
352     my ($self, @extra_args) = @_;
353    
354 root 1.4 return 0 if $self->{fh};
355 root 1.1
356     # cache optionlist for same "path"
357 root 1.2 ($mpv_path, $mpv_optionlist) = ($self->{mpv}, scalar qx{\Q$self->{mpv}\E --list-options})
358 root 1.1 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 root 1.2 AnyEvent::Util::fh_nonblocking $fh, 1;
366 root 1.1
367 root 1.2 $self->{pid} = fork;
368 root 1.1
369     if ($self->{pid} eq 0) {
370 root 1.2 AnyEvent::Util::fh_nonblocking $slave, 0;
371     fcntl $slave, Fcntl::F_SETFD, 0;
372    
373 root 1.1 my $input_file = $options =~ /\s--input-ipc-client\s/ ? "input-ipc-client" : "input-file";
374    
375     exec $self->{mpv},
376 root 1.5 qw(--no-input-terminal),
377 root 1.1 ($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 root 1.2 my $trace = delete $self->{trace} || sub { };
387 root 1.1
388     $trace = sub { warn "$_[0] $_[1]\n" } if $trace && !ref $trace;
389    
390     my $buf;
391     my $wbuf;
392 root 1.2
393     Scalar::Util::weaken $self;
394 root 1.1
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 root 1.5 my $reply = $JSON->new->latin1->decode ($1);
403 root 1.1
404     if (exists $reply->{event}) {
405     if (
406     $reply->{event} eq "client-message"
407     and $reply->{args}[0] eq "AnyEvent::MPV"
408     ) {
409 root 1.3 if ($reply->{args}[1] eq "key") {
410 root 1.4 (my $key = $reply->{args}[2]) =~ s/\\x(..)/chr hex $1/ge;
411     $self->on_key ($key);
412 root 1.3 }
413 root 1.1 } else {
414 root 1.5 $self->on_event ($reply->{event}, $reply);
415 root 1.1 }
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 root 1.2 $self->stop;
443 root 1.1 $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 root 1.4
459     1
460 root 1.1 }
461    
462 root 1.5 =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 root 1.1 sub on_eof {
498     my ($self) = @_;
499    
500     $self->{on_eof}($self) if $self->{on_eof};
501     }
502    
503 root 1.5 =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 event
510     name (same as C<< $data->{event} >>, purely for convenience), and the
511     third argument is the full event object as sent by F<mpv>. See L<List of
512     events|https://mpv.io/manual/stable/#list-of-events> in its documentation.
513    
514     For subclassing, see I<SUBCLASSING>, below.
515    
516     =cut
517    
518 root 1.1 sub on_event {
519     my ($self, $key) = @_;
520    
521     $self->{on_event}($self, $key) if $self->{on_event};
522     }
523    
524 root 1.5 =item $mpv->on_key ($string)
525    
526     Invoked when a key declared by C<< ->bind_key >> is pressed. The default
527     invokes the C<on_key> code reference specified in the constructor with the
528     C<$mpv> object and the key name as arguments, or do nothing if none was
529     given.
530    
531     For more details and examples, see the C<bind_key> method.
532    
533     For subclassing, see I<SUBCLASSING>, below.
534    
535     =cut
536    
537 root 1.2 sub on_key {
538 root 1.1 my ($self, $key) = @_;
539    
540 root 1.2 $self->{on_key}($self, $key) if $self->{on_key};
541 root 1.1 }
542    
543 root 1.5 =item $mpv->cmd ($command => $arg, $arg...)
544    
545     Queues a command to be sent to F<mpv>, using the given arguments, and
546     immediately return a condvar.
547    
548     See L<the mpv
549     documentation|https://mpv.io/manual/stable/#list-of-input-commands> for
550     details on individual commands.
551    
552     The condvar can be ignored:
553    
554     $mpv->cmd (set_property => "deinterlace", "yes");
555    
556     Or it can be used to synchronously wait for the command results:
557    
558     $cv = $mpv->cmd (get_property => "video-format");
559     $format = $cv->recv;
560    
561     # or simpler:
562    
563     $format = $mpv->cmd (get_property => "video-format")->recv;
564    
565     # or even simpler:
566    
567     $format = $mpv->cmd_recv (get_property => "video-format");
568    
569     Or you can set a callback:
570    
571     $cv = $mpv->cmd (get_property => "video-format");
572     $cv->cb (sub {
573     my $format = $_[0]->recv;
574     });
575    
576     On error, the condvar will croak when C<recv> is called.
577    
578     =cut
579    
580 root 1.1 sub cmd {
581     my ($self, @cmd) = @_;
582    
583     my $cv = AE::cv;
584    
585     my $reqid = ++$self->{reqid};
586     $self->{cmd_cv}{$reqid} = $cv;
587    
588 root 1.5 my $cmd = $JSON->new->utf8->encode ({ command => ref $cmd[0] ? $cmd[0] : \@cmd, request_id => $reqid*1 });
589 root 1.1
590     # (un-)apply escape_binary hack
591     $cmd =~ s/\xf4\x8e\x97\x9f(..)/sprintf sprintf "\\x%02x", hex $1/ges; # f48e979f == 10e5df in utf-8
592    
593     $self->{_send}($cmd);
594    
595     $cv
596     }
597    
598 root 1.5 =item $result = $mpv->cmd_recv ($command => $arg, $arg...)
599    
600     The same as calling C<cmd> and immediately C<recv> on its return
601     value. Useful when you don't want to mess with F<mpv> asynchronously or
602     simply needs to have the result:
603    
604     $mpv->cmd_recv ("stop");
605     $position = $mpv->cmd_recv ("get_property", "playback-time");
606    
607     =cut
608    
609 root 1.4 sub cmd_recv {
610     &cmd->recv
611     }
612    
613 root 1.5 =item $mpv->bind_key ($INPUT => $string)
614    
615     This is an extension implement by this module to make it easy to get key events. The way this is implemented
616     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
617     passed ot the C<on_key> handle when the key is proessed, e.g.:
618    
619     my $mpv = AnyEvent::MPV->new (
620     on_key => sub {
621     my ($mpv, $key) = @_;
622    
623     if ($key eq "letmeout") {
624     print "user pressed escape\n";
625     }
626     },
627     );
628    
629     $mpv_>bind_key (ESC => "letmeout");
630    
631     The key configuration is lost when F<mpv> is stopped and must be (re-)done
632     after every C<start>.
633    
634     =cut
635    
636 root 1.4 sub bind_key {
637     my ($self, $key, $event) = @_;
638    
639     $event =~ s/([^A-Za-z0-9\-_])/sprintf "\\x%02x", ord $1/ge;
640     $self->cmd (keybind => $key => "no-osd script-message AnyEvent::MPV key $event");
641     }
642    
643 root 1.5 =back
644 root 1.1
645 root 1.5 =head2 SUBCLASSING
646 root 1.1
647 root 1.5 Like most perl objects, C<AnyEvent::MPV> objects are implemented as
648     hashes, with the constructor simply storing all passed key-value pairs in
649     the object. If you want to subclass to provide your own C<on_*> methods,
650     be my guest and rummage around in the internals as much as you wish - the
651     only guarantee that this module dcoes is that it will not use keys with
652     double colons in the name, so youc an use those, or chose to simply not
653     care and deal with the breakage.
654 root 1.1
655 root 1.5 If you don't want to go to the effort of subclassing this module, you can
656     also specify all event handlers as constructor keys.
657 root 1.1
658     =head1 SEE ALSO
659    
660 root 1.5 L<AnyEvent>, L<the mpv command documentation|https://mpv.io/manual/stable/#command-interface>.
661 root 1.1
662     =head1 AUTHOR
663    
664     Marc Lehmann <schmorp@schmorp.de>
665     http://home.schmorp.de/
666    
667     =cut
668    
669     1
670