ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MPV/MPV.pm
Revision: 1.8
Committed: Mon Mar 20 11:12:40 2023 UTC (13 months, 4 weeks ago) by root
Branch: MAIN
Changes since 1.7: +24 -17 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 root 1.2
392     Scalar::Util::weaken $self;
393 root 1.1
394     $self->{rw} = AE::io $fh, 0, sub {
395     if (sysread $fh, $buf, 8192, length $buf) {
396     while ($buf =~ s/^([^\n]+)\n//) {
397     $trace->("mpv>" => "$1");
398    
399     if ("{" eq substr $1, 0, 1) {
400     eval {
401 root 1.5 my $reply = $JSON->new->latin1->decode ($1);
402 root 1.1
403     if (exists $reply->{event}) {
404     if (
405     $reply->{event} eq "client-message"
406     and $reply->{args}[0] eq "AnyEvent::MPV"
407     ) {
408 root 1.3 if ($reply->{args}[1] eq "key") {
409 root 1.4 (my $key = $reply->{args}[2]) =~ s/\\x(..)/chr hex $1/ge;
410     $self->on_key ($key);
411 root 1.3 }
412 root 1.1 } else {
413 root 1.7 $self->on_event (delete $reply->{event}, $reply);
414 root 1.1 }
415     } elsif (exists $reply->{request_id}) {
416     my $cv = delete $self->{cmd_cv}{$reply->{request_id}};
417    
418     unless ($cv) {
419     warn "no cv found for request id <$reply->{request_id}>\n";
420     next;
421     }
422    
423     if (exists $reply->{data}) {
424     $cv->send ($reply->{data});
425     } elsif ($reply->{error} eq "success") { # success means error... eh.. no...
426     $cv->send;
427     } else {
428     $cv->croak ($reply->{error});
429     }
430    
431     } else {
432     warn "unexpected reply from mpv, pleasew report: <$1>\n";
433     }
434     };
435     warn $@ if $@;
436     } else {
437     $trace->("mpv>" => "$1");
438     }
439     }
440     } else {
441 root 1.2 $self->stop;
442 root 1.1 $self->on_eof;
443     }
444     };
445    
446 root 1.8 my $wbuf;
447     my $reqid;
448    
449     $self->{_cmd} = sub {
450     my $cv = AE::cv;
451    
452     $self->{cmd_cv}{++$reqid} = $cv;
453    
454     my $cmd = $JSON->new->utf8->encode ({ command => ref $_[0] ? $_[0] : \@_, request_id => $reqid*1 });
455    
456     # (un-)apply escape_binary hack
457     $cmd =~ s/\xf4\x8e\x97\x9f(..)/sprintf sprintf "\\x%02x", hex $1/ges; # f48e979f == 10e5df in utf-8
458    
459     $wbuf .= "$cmd\n";
460 root 1.1
461     $trace->(">mpv" => "$_[0]");
462    
463     $self->{ww} ||= AE::io $fh, 1, sub {
464     my $len = syswrite $fh, $wbuf;
465     substr $wbuf, 0, $len, "";
466     undef $self->{ww} unless length $wbuf;
467     };
468 root 1.8
469     $cv
470 root 1.1 };
471 root 1.4
472     1
473 root 1.1 }
474    
475 root 1.8 sub DESTROY {
476     $_[0]->stop;
477     }
478    
479 root 1.5 =item $mpv->stop
480    
481     Ensures that F<mpv> is being stopped, by killing F<mpv> with a C<TERM>
482     signal if needed. After this, you can C<< ->start >> a new instance again.
483    
484     =cut
485    
486     sub stop {
487     my ($self) = @_;
488    
489     delete $self->{rw};
490     delete $self->{ww};
491    
492     if ($self->{pid}) {
493    
494     close delete $self->{fh}; # current mpv versions should cleanup on their own on close
495    
496     kill TERM => $self->{pid};
497    
498     }
499    
500     delete $self->{pid};
501     delete $self->{cmd_cv};
502 root 1.8 delete $self->{obsid};
503     delete $self->{wbuf};
504 root 1.5 }
505    
506     =item $mpv->on_eof
507    
508     This method is called when F<mpv> quits - usually unexpectedly. The
509     default implementation will call the C<on_eof> code reference specified in
510     the constructor, or do nothing if none was given.
511    
512     For subclassing, see I<SUBCLASSING>, below.
513    
514     =cut
515    
516 root 1.1 sub on_eof {
517     my ($self) = @_;
518    
519     $self->{on_eof}($self) if $self->{on_eof};
520     }
521    
522 root 1.5 =item $mpv->on_event ($event, $data)
523    
524     This method is called when F<mpv> sends an asynchronous event. The default
525     implementation will call the C<on_event> code reference specified in the
526     constructor, or do nothing if none was given.
527    
528 root 1.7 The first/implicit argument is the C<$mpv> object, the second is the
529     event name (same as C<< $data->{event} >>, purely for convenience), and
530     the third argument is the event object as sent by F<mpv> (sans C<event>
531     key). See L<List of events|https://mpv.io/manual/stable/#list-of-events>
532     in its documentation.
533 root 1.5
534     For subclassing, see I<SUBCLASSING>, below.
535    
536     =cut
537    
538 root 1.1 sub on_event {
539     my ($self, $key) = @_;
540    
541     $self->{on_event}($self, $key) if $self->{on_event};
542     }
543    
544 root 1.5 =item $mpv->on_key ($string)
545    
546     Invoked when a key declared by C<< ->bind_key >> is pressed. The default
547     invokes the C<on_key> code reference specified in the constructor with the
548     C<$mpv> object and the key name as arguments, or do nothing if none was
549     given.
550    
551     For more details and examples, see the C<bind_key> method.
552    
553     For subclassing, see I<SUBCLASSING>, below.
554    
555     =cut
556    
557 root 1.2 sub on_key {
558 root 1.1 my ($self, $key) = @_;
559    
560 root 1.2 $self->{on_key}($self, $key) if $self->{on_key};
561 root 1.1 }
562    
563 root 1.5 =item $mpv->cmd ($command => $arg, $arg...)
564    
565     Queues a command to be sent to F<mpv>, using the given arguments, and
566     immediately return a condvar.
567    
568     See L<the mpv
569     documentation|https://mpv.io/manual/stable/#list-of-input-commands> for
570     details on individual commands.
571    
572     The condvar can be ignored:
573    
574     $mpv->cmd (set_property => "deinterlace", "yes");
575    
576     Or it can be used to synchronously wait for the command results:
577    
578     $cv = $mpv->cmd (get_property => "video-format");
579     $format = $cv->recv;
580    
581     # or simpler:
582    
583     $format = $mpv->cmd (get_property => "video-format")->recv;
584    
585     # or even simpler:
586    
587     $format = $mpv->cmd_recv (get_property => "video-format");
588    
589     Or you can set a callback:
590    
591     $cv = $mpv->cmd (get_property => "video-format");
592     $cv->cb (sub {
593     my $format = $_[0]->recv;
594     });
595    
596     On error, the condvar will croak when C<recv> is called.
597    
598     =cut
599    
600 root 1.1 sub cmd {
601 root 1.8 my $self = shift;
602 root 1.1
603 root 1.8 $self->{_cmd}->(@_)
604 root 1.1 }
605    
606 root 1.5 =item $result = $mpv->cmd_recv ($command => $arg, $arg...)
607    
608     The same as calling C<cmd> and immediately C<recv> on its return
609     value. Useful when you don't want to mess with F<mpv> asynchronously or
610     simply needs to have the result:
611    
612     $mpv->cmd_recv ("stop");
613     $position = $mpv->cmd_recv ("get_property", "playback-time");
614    
615     =cut
616    
617 root 1.4 sub cmd_recv {
618     &cmd->recv
619     }
620    
621 root 1.5 =item $mpv->bind_key ($INPUT => $string)
622    
623     This is an extension implement by this module to make it easy to get key events. The way this is implemented
624     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
625     passed ot the C<on_key> handle when the key is proessed, e.g.:
626    
627     my $mpv = AnyEvent::MPV->new (
628     on_key => sub {
629     my ($mpv, $key) = @_;
630    
631     if ($key eq "letmeout") {
632     print "user pressed escape\n";
633     }
634     },
635     );
636    
637     $mpv_>bind_key (ESC => "letmeout");
638    
639     The key configuration is lost when F<mpv> is stopped and must be (re-)done
640     after every C<start>.
641    
642     =cut
643    
644 root 1.4 sub bind_key {
645     my ($self, $key, $event) = @_;
646    
647     $event =~ s/([^A-Za-z0-9\-_])/sprintf "\\x%02x", ord $1/ge;
648     $self->cmd (keybind => $key => "no-osd script-message AnyEvent::MPV key $event");
649     }
650    
651 root 1.5 =back
652 root 1.1
653 root 1.5 =head2 SUBCLASSING
654 root 1.1
655 root 1.5 Like most perl objects, C<AnyEvent::MPV> objects are implemented as
656     hashes, with the constructor simply storing all passed key-value pairs in
657     the object. If you want to subclass to provide your own C<on_*> methods,
658     be my guest and rummage around in the internals as much as you wish - the
659     only guarantee that this module dcoes is that it will not use keys with
660     double colons in the name, so youc an use those, or chose to simply not
661     care and deal with the breakage.
662 root 1.1
663 root 1.5 If you don't want to go to the effort of subclassing this module, you can
664     also specify all event handlers as constructor keys.
665 root 1.1
666     =head1 SEE ALSO
667    
668 root 1.5 L<AnyEvent>, L<the mpv command documentation|https://mpv.io/manual/stable/#command-interface>.
669 root 1.1
670     =head1 AUTHOR
671    
672     Marc Lehmann <schmorp@schmorp.de>
673     http://home.schmorp.de/
674    
675     =cut
676    
677     1
678