ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MPV/README
(Generate patch)

Comparing AnyEvent-MPV/README (file contents):
Revision 1.2 by root, Sun Mar 19 23:26:01 2023 UTC vs.
Revision 1.5 by root, Sun Aug 11 02:54:52 2024 UTC

1NAME 1NAME
2 AnyEvent::MPV - remote control mpv (https://mpv.io) 2 AnyEvent::MPV - remote control mpv (https://mpv.io)
3 3
4SYNOPSIS 4SYNOPSIS
5 use AnyEvent::MPV; 5 use AnyEvent::MPV;
6
7 my $videofile = "path/to/file.mkv";
8 use AnyEvent;
9 my $mpv = AnyEvent::MPV->new (trace => 1);
10 $mpv->start ("--idle=yes");
11 $mpv->cmd (loadfile => $mpv->escape_binary ($videofile));
12 my $quit = AE::cv;
13 $mpv->register_event (end_file => $quit);
14 $quit->recv;
6 15
7DESCRIPTION 16DESCRIPTION
8 This module allows you to remote control mpv (a video player). It also 17 This module allows you to remote control mpv (a video player). It also
9 is an AnyEvent user, you need to make sure that you use and run a 18 is an AnyEvent user, you need to make sure that you use and run a
10 supported event loop. 19 supported event loop.
39 Here is a very simple client: 48 Here is a very simple client:
40 49
41 use AnyEvent; 50 use AnyEvent;
42 use AnyEvent::MPV; 51 use AnyEvent::MPV;
43 52
44 my $videofile = "./xyzzy.mp4"; 53 my $videofile = "./xyzzy.mkv";
45 54
46 my $mpv = AnyEvent::MPV->new (trace => 1); 55 my $mpv = AnyEvent::MPV->new (trace => 1);
47 56
48 $mpv->start ("--", $videofile); 57 $mpv->start ("--", $videofile);
49 58
74 shell command), so let us load the file at runtime: 83 shell command), so let us load the file at runtime:
75 84
76 use AnyEvent; 85 use AnyEvent;
77 use AnyEvent::MPV; 86 use AnyEvent::MPV;
78 87
79 my $videofile = "./xyzzy.mp4"; 88 my $videofile = "./xyzzy.mkv";
80 89
81 my $mpv = AnyEvent::MPV->new ( 90 my $mpv = AnyEvent::MPV->new (
82 trace => 1, 91 trace => 1,
83 args => ["--pause", "--idle=yes"], 92 args => ["--pause", "--idle=yes"],
84 ); 93 );
90 my $timer = AE::timer 2, 0, my $quit = AE::cv; 99 my $timer = AE::timer 2, 0, my $quit = AE::cv;
91 $quit->recv; 100 $quit->recv;
92 101
93 This specifies extra arguments in the constructor - these arguments are 102 This specifies extra arguments in the constructor - these arguments are
94 used every time you "->start" mpv, while the arguments to "->start" are 103 used every time you "->start" mpv, while the arguments to "->start" are
95 only used for this specific clal to0 "start". The argument --pause keeps 104 only used for this specific call to "start". The argument --pause keeps
96 mpv in pause mode (i.e. it does not play the file after loading it), and 105 mpv in pause mode (i.e. it does not play the file after loading it), and
97 "--idle=yes" tells mpv to not quit when it does not have a playlist - as 106 "--idle=yes" tells mpv to not quit when it does not have a playlist - as
98 no files are specified on the command line. 107 no files are specified on the command line.
99 108
100 To load a file, we then send it a "loadfile" command, which accepts, as 109 To load a file, we then send it a "loadfile" command, which accepts, as
101 first argument, the URL or path to a video file. To make sure mpv does 110 first argument, the URL or path to a video file. To make sure mpv does
102 not misinterpret the path as a URL, it was prefixed with ./ (similarly 111 not misinterpret the path as a URL, it was prefixed with ./ (similarly
103 to "protecting" paths in perls "open"). 112 to "protecting" paths in perls "open").
104 113
105 Since commands send *to* mpv are send in UTF-8, we need to escape the 114 Since commands send *to* mpv are send in UTF-8, we need to escape the
106 filename (which might be in any encoding) using the "esscape_binary" 115 filename (which might be in any encoding) using the "escape_binary"
107 method - this is not needed if your filenames are just ascii, or 116 method - this is not needed if your filenames are just ASCII, or
108 magically get interpreted correctly, but if you accept arbitrary 117 magically get interpreted correctly, but if you accept arbitrary
109 filenamews (e.g. from the user), you need to do this. 118 filenames (e.g. from the user), you need to do this.
110 119
111 The "cmd_recv" method then queues the command, waits for a reply and 120 The "cmd_recv" method then queues the command, waits for a reply and
112 returns the reply data (or croaks on error). mpv would, at this point, 121 returns the reply data (or croaks on error). mpv would, at this point,
113 load the file and, if everything was successful, show the first frame 122 load the file and, if everything was successful, show the first frame
114 and pause. Note that, since mpv is implement rather synchronously 123 and pause. Note that, since mpv is implement rather synchronously
117 the "loadfile" command itself will run successfully. 126 the "loadfile" command itself will run successfully.
118 127
119 To unpause, we send another command, "set", to set the "pause" property 128 To unpause, we send another command, "set", to set the "pause" property
120 to "no", this time using the "cmd" method, which queues the command, but 129 to "no", this time using the "cmd" method, which queues the command, but
121 instead of waiting for a reply, it immediately returns a condvar that 130 instead of waiting for a reply, it immediately returns a condvar that
122 cna be used to receive results. 131 can be used to receive results.
123 132
124 This should then cause mpv to start playing the video. 133 This should then cause mpv to start playing the video.
125 134
126 It then again waits two seconds and quits. 135 It then again waits two seconds and quits.
127 136
129 receiving events (using a somewhat embellished example): 138 receiving events (using a somewhat embellished example):
130 139
131 use AnyEvent; 140 use AnyEvent;
132 use AnyEvent::MPV; 141 use AnyEvent::MPV;
133 142
134 my $videofile = "xyzzy.mp4"; 143 my $videofile = "xyzzy.mkv";
135 144
136 my $quit = AE::cv; 145 my $quit = AE::cv;
137 146
138 my $mpv = AnyEvent::MPV->new ( 147 my $mpv = AnyEvent::MPV->new (
139 trace => 1, 148 trace => 1,
140 args => ["--pause", "--idle=yes"], 149 args => ["--pause", "--idle=yes"],
141 on_event => sub {
142 my ($mpv, $event, $data) = @_;
143
144 if ($event eq "start-file") {
145 $mpv->cmd ("set", "pause", "no");
146 } elsif ($event eq "end-file") {
147 print "end-file<$data->{reason}>\n";
148 $quit->send;
149 }
150 },
151 ); 150 );
152 151
153 $mpv->start; 152 $mpv->start;
153
154 $mpv->register_event (start_file => sub {
155 $mpv->cmd ("set", "pause", "no");
156 });
157
158 $mpv->register_event (end_file => sub {
159 my ($mpv, $event, $data) = @_;
160
161 print "end-file<$data->{reason}>\n";
162 $quit->send;
163 });
164
154 $mpv->cmd (loadfile => $mpv->escape_binary ($videofile)); 165 $mpv->cmd (loadfile => $mpv->escape_binary ($videofile));
155 166
156 $quit->recv; 167 $quit->recv;
157 168
158 This example uses a global condvar $quit to wait for the file to finish 169 This example uses a global condvar $quit to wait for the file to finish
159 playing. Also, most of the logic is now in an "on_event" callback, which 170 playing. Also, most of the logic is now implement in event handlers.
160 receives an event name and the actual event object.
161 171
162 The two events we handle are "start-file", which is emitted by mpv once 172 The two events handlers we register are "start-file", which is emitted
163 it has loaded a new file, and "end-file", which signals the end of a 173 by mpv once it has loaded a new file, and "end-file", which signals the
164 file. 174 end of a file (underscores are internally replaced by minus signs, so
175 you can specify event names with either).
165 176
166 In the former event, we again set the "pause" property to "no" so the 177 In the "start-file" event, we again set the "pause" property to "no" so
167 movie starts playing. For the latter event, we tell the main program to 178 the movie starts playing. For the "end-file" event, we tell the main
168 quit by invoking $quit. 179 program to quit by invoking $quit.
169 180
170 This should conclude the basics of operation. There are a few more 181 This should conclude the basics of operation. There are a few more
171 examples later in the documentation. 182 examples later in the documentation.
172 183
173 ENCODING CONVENTIONS 184 ENCODING CONVENTIONS
174 As a rule of thumb, all data you pass to this module to be sent to mpv 185 As a rule of thumb, all data you pass to this module to be sent to mpv
175 is expected to be in unicode. To pass something that isn't, you need to 186 is expected to be in unicode. To pass something that isn't, you need to
176 escape it using "escape_binary". 187 escape it using "escape_binary".
177 188
178 Data received from $mpv, however, is *not* decoded to unicode, as data 189 Data received from mpv, however, is *not* decoded to unicode, as data
179 returned by mpv is not generally encoded in unicode, and the encoding is 190 returned by mpv is not generally encoded in unicode, and the encoding is
180 usually unspecified. So if you receive data and expect it to be in 191 usually unspecified. So if you receive data and expect it to be in
181 unicode, you need to first decode it from UTF-8, but note that this 192 unicode, you need to first decode it from UTF-8, but note that this
182 might fail. This is not a limitation of this module - mpv simply does 193 might fail. This is not a limitation of this module - mpv simply does
183 not specify nor guarantee a specific encoding, or any encoding at all, 194 not specify nor guarantee a specific encoding, or any encoding at all,
205 Enables tracing if true. In trace mode, output from mpv is 216 Enables tracing if true. In trace mode, output from mpv is
206 printed to standard error using a "mpv>" prefix, and commands 217 printed to standard error using a "mpv>" prefix, and commands
207 sent to mpv are printed with a ">mpv" prefix. 218 sent to mpv are printed with a ">mpv" prefix.
208 219
209 If a code reference is passed, then instead of printing to 220 If a code reference is passed, then instead of printing to
210 standard errort, this coderef is invoked with a first arfgument 221 standard error, this coderef is invoked with a first argument
211 being either "mpv>" or ">mpv", and the second argument being a 222 being either "mpv>" or ">mpv", and the second argument being a
212 string to display. The default implementation simply does this: 223 string to display. The default implementation simply does this:
213 224
214 sub { 225 sub {
215 warn "$_[0] $_[1]\n"; 226 warn "$_[0] $_[1]\n";
220 on_key => $coderef->($mpv, $string) 231 on_key => $coderef->($mpv, $string)
221 These are invoked by the default method implementation of the 232 These are invoked by the default method implementation of the
222 same name - see below. 233 same name - see below.
223 234
224 $string = $mpv->escape_binary ($string) 235 $string = $mpv->escape_binary ($string)
225 This module excects all command data sent to mpv to be in unicode. 236 This module expects all command data sent to mpv to be in unicode.
226 Some things are not, such as filenames. To pass binary data such as 237 Some things are not, such as filenames. To pass binary data such as
227 filenames through a comamnd, you need to escape it using this 238 filenames through a command, you need to escape it using this
228 method. 239 method.
229 240
230 The simplest example is a "loadfile" command: 241 The simplest example is a "loadfile" command:
231 242
232 $mpv->cmd_recv (loadfile => $mpv->escape_binary ($path)); 243 $mpv->cmd_recv (loadfile => $mpv->escape_binary ($path));
233 244
234 $started = $mpv->start (argument...) 245 $started = $mpv->start (argument...)
235 Starts mpv, passing the given arguemnts as extra arguments to mpv. 246 Starts mpv, passing the given arguments as extra arguments to mpv.
236 If mpv is already running, it returns false, otherwise it returns a 247 If mpv is already running, it returns false, otherwise it returns a
237 true value, so you can easily start mpv on demand by calling "start" 248 true value, so you can easily start mpv on demand by calling "start"
238 just before using it, and if it is already running, it will not be 249 just before using it, and if it is already running, it will not be
239 started again. 250 started again.
240 251
241 The arguments passwd to mpv are a set of hardcoded built-in 252 The arguments passed to mpv are a set of hard-coded built-in
242 arguments, followed by the arguments specified in the constructor, 253 arguments, followed by the arguments specified in the constructor,
243 followed by the arguments passwd to this method. The built-in 254 followed by the arguments passed to this method. The built-in
244 arguments currently are --no-input-terminal, --really-quiet (or 255 arguments currently are --no-input-terminal, --really-quiet (or
245 --quiet in "trace" mode), and "--input-ipc-client" (or equivalent). 256 --quiet in "trace" mode), and "--input-ipc-client" (or equivalent).
246 257
247 Some commonly used and/or even useful arguments you might want to 258 Some commonly used and/or even useful arguments you might want to
248 pass are: 259 pass are:
289 default implementation will call the "on_event" code reference 300 default implementation will call the "on_event" code reference
290 specified in the constructor, or do nothing if none was given. 301 specified in the constructor, or do nothing if none was given.
291 302
292 The first/implicit argument is the $mpv object, the second is the 303 The first/implicit argument is the $mpv object, the second is the
293 event name (same as "$data->{event}", purely for convenience), and 304 event name (same as "$data->{event}", purely for convenience), and
294 the third argument is the full event object as sent by mpv. See List 305 the third argument is the event object as sent by mpv (sans "event"
306 key). See List of events
295 of events <https://mpv.io/manual/stable/#list-of-events> in its 307 <https://mpv.io/manual/stable/#list-of-events> in its documentation.
296 documentation.
297 308
298 For subclassing, see *SUBCLASSING*, below. 309 For subclassing, see *SUBCLASSING*, below.
299 310
300 $mpv->on_key ($string) 311 $mpv->on_key ($string)
301 Invoked when a key declared by "->bind_key" is pressed. The default 312 Invoked when a key declared by "->bind_key" is pressed. The default
350 $position = $mpv->cmd_recv ("get_property", "playback-time"); 361 $position = $mpv->cmd_recv ("get_property", "playback-time");
351 362
352 $mpv->bind_key ($INPUT => $string) 363 $mpv->bind_key ($INPUT => $string)
353 This is an extension implement by this module to make it easy to get 364 This is an extension implement by this module to make it easy to get
354 key events. The way this is implemented is to bind a 365 key events. The way this is implemented is to bind a
355 "client-message" witha first argument of "AnyEvent::MPV" and the 366 "client-message" with a first argument of "AnyEvent::MPV" and the
356 $string you passed. This $string is then passed ot the "on_key" 367 $string you passed. This $string is then passed to the "on_key"
357 handle when the key is proessed, e.g.: 368 handle when the key is processed, e.g.:
358 369
359 my $mpv = AnyEvent::MPV->new ( 370 my $mpv = AnyEvent::MPV->new (
360 on_key => sub { 371 on_key => sub {
361 my ($mpv, $key) = @_; 372 my ($mpv, $key) = @_;
362 373
366 }, 377 },
367 ); 378 );
368 379
369 $mpv_>bind_key (ESC => "letmeout"); 380 $mpv_>bind_key (ESC => "letmeout");
370 381
382 You can find a list of key names in the mpv documentation
383 <https://mpv.io/manual/stable/#key-names>.
384
371 The key configuration is lost when mpv is stopped and must be 385 The key configuration is lost when mpv is stopped and must be
372 (re-)done after every "start". 386 (re-)done after every "start".
387
388 [$guard] = $mpv->register_event ($event => $coderef->($mpv, $event,
389 $data))
390 This method registers a callback to be invoked for a specific event.
391 Whenever the event occurs, it calls the coderef with the $mpv
392 object, the $event name and the event object, just like the
393 "on_event" method.
394
395 For a list of events, see the mpv documentation
396 <https://mpv.io/manual/stable/#list-of-events>. Any underscore in
397 the event name is replaced by a minus sign, so you can specify event
398 names using underscores for easier quoting in Perl.
399
400 In void context, the handler stays registered until "stop" is
401 called. In any other context, it returns a guard object that, when
402 destroyed, will unregister the handler.
403
404 You can register multiple handlers for the same event, and this
405 method does not interfere with the "on_event" mechanism. That is,
406 you can completely ignore this method and handle events in a
407 "on_event" handler, or mix both approaches as you see fit.
408
409 Note that unlike commands, event handlers are registered
410 immediately, that is, you can issue a command, then register an
411 event handler and then get an event for this handler *before* the
412 command is even sent to mpv. If this kind of race is an issue, you
413 can issue a dummy command such as "get_version" and register the
414 handler when the reply is received.
415
416 [$guard] = $mpv->observe_property ($name => $coderef->($mpv, $name,
417 $value))
418 [$guard] = $mpv->observe_property_string ($name => $coderef->($mpv,
419 $name, $value))
420 These methods wrap a registry system around mpv's "observe_property"
421 and "observe_property_string" commands - every time the named
422 property changes, the coderef is invoked with the $mpv object, the
423 name of the property and the new value.
424
425 For a list of properties that you can observe, see the mpv
426 documentation <https://mpv.io/manual/stable/#property-list>.
427
428 Due to the (sane :) way mpv handles these requests, you will always
429 get a property change event right after registering an observer
430 (meaning you don't have to query the current value), and it is also
431 possible to register multiple observers for the same property - they
432 will all be handled properly.
433
434 When called in void context, the observer stays in place until mpv
435 is stopped. In any other context, these methods return a guard
436 object that, when it goes out of scope, unregisters the observe
437 using "unobserve_property".
438
439 Internally, this method uses observer ids of 2**52
440 (0x10000000000000) or higher - it will not interfere with lower
441 observer ids, so it is possible to completely ignore this system and
442 execute "observe_property" commands yourself, whilst listening to
443 "property-change" events - as long as your ids stay below 2**52.
444
445 Example: register observers for changes in "aid" and "sid". Note
446 that a dummy statement is added to make sure the method is called in
447 void context.
448
449 sub register_observers {
450 my ($mpv) = @_;
451
452 $mpv->observe_property (aid => sub {
453 my ($mpv, $name, $value) = @_;
454 print "property aid (=$name) has changed to $value\n";
455 });
456
457 $mpv->observe_property (sid => sub {
458 my ($mpv, $name, $value) = @_;
459 print "property sid (=$name) has changed to $value\n";
460 });
461
462 () # ensure the above method is called in void context
463 }
373 464
374 SUBCLASSING 465 SUBCLASSING
375 Like most perl objects, "AnyEvent::MPV" objects are implemented as 466 Like most perl objects, "AnyEvent::MPV" objects are implemented as
376 hashes, with the constructor simply storing all passed key-value pairs 467 hashes, with the constructor simply storing all passed key-value pairs
377 in the object. If you want to subclass to provide your own "on_*" 468 in the object. If you want to subclass to provide your own "on_*"
378 methods, be my guest and rummage around in the internals as much as you 469 methods, be my guest and rummage around in the internals as much as you
379 wish - the only guarantee that this module dcoes is that it will not use 470 wish - the only guarantee that this module does is that it will not use
380 keys with double colons in the name, so youc an use those, or chose to 471 keys with double colons in the name, so you can use those, or chose to
381 simply not care and deal with the breakage. 472 simply not care and deal with the breakage.
382 473
383 If you don't want to go to the effort of subclassing this module, you 474 If you don't want to go to the effort of subclassing this module, you
384 can also specify all event handlers as constructor keys. 475 can also specify all event handlers as constructor keys.
476
477EXAMPLES
478 Here are some real-world code snippets, thrown in here mainly to give
479 you some example code to copy.
480
481 doomfrontend
482 At one point I replaced mythtv-frontend by my own terminal-based video
483 player (based on rxvt-unicode). I toyed with the idea of using mpv's
484 subtitle engine to create the user interface, but that is hard to use
485 since you don't know how big your letters are. It is also where most of
486 this modules code has originally been developed in.
487
488 It uses a unified input queue to handle various remote controls, so its
489 event handling needs are very simple - it simply feeds all events into
490 the input queue:
491
492 my $mpv = AnyEvent::MPV->new (
493 mpv => $MPV,
494 args => \@MPV_ARGS,
495 on_event => sub {
496 input_feed "mpv/$_[1]", $_[2];
497 },
498 on_key => sub {
499 input_feed $_[1];
500 },
501 on_eof => sub {
502 input_feed "mpv/quit";
503 },
504 );
505
506 ...
507
508 $mpv->start ("--idle=yes", "--pause", "--force-window=no");
509
510 It also doesn't use complicated command line arguments - the file search
511 options have the most impact, as they prevent mpv from scanning
512 directories with tens of thousands of files for subtitles and more:
513
514 --audio-client-name=doomfrontend
515 --osd-on-seek=msg-bar --osd-bar-align-y=-0.85 --osd-bar-w=95
516 --sub-auto=exact --audio-file-auto=exact
517
518 Since it runs on a TV without a desktop environment, it tries to keep
519 complications such as dbus away and the screensaver happy:
520
521 # prevent xscreensaver from doing something stupid, such as starting dbus
522 $ENV{DBUS_SESSION_BUS_ADDRESS} = "/"; # prevent dbus autostart for sure
523 $ENV{XDG_CURRENT_DESKTOP} = "generic";
524
525 It does bind a number of keys to internal (to doomfrontend) commands:
526
527 for (
528 List::Util::pairs qw(
529 ESC return
530 q return
531 ENTER enter
532 SPACE pause
533 [ steprev
534 ] stepfwd
535 j subtitle
536 BS red
537 i green
538 o yellow
539 b blue
540 D triangle
541 UP up
542 DOWN down
543 RIGHT right
544 LEFT left
545 ),
546 (map { ("KP$_" => "num$_") } 0..9),
547 KP_INS => 0, # KP0, but different
548 ) {
549 $mpv->bind_key ($_->[0] => $_->[1]);
550 }
551
552 It also reacts to sponsorblock chapters, so it needs to know when video
553 chapters change. Predating "AnyEvent::MPV", it handles observers
554 manually instead of using "observe_property":
555
556 $mpv->cmd (observe_property => 1, "chapter-metadata");
557
558 It also tries to apply an mpv profile, if it exists:
559
560 eval {
561 # the profile is optional
562 $mpv->cmd ("apply-profile" => "doomfrontend");
563 };
564
565 Most of the complicated parts deal with saving and restoring per-video
566 data, such as bookmarks, playing position, selected audio and subtitle
567 tracks and so on. However, since it uses Coro, it can conveniently block
568 and wait for replies, which is not possible in purely event based
569 programs, as you are not allowed to block inside event callbacks in most
570 event loops. This simplifies the code quite a bit.
571
572 When the file to be played is a TV recording done by mythtv, it uses the
573 "appending" protocol and deinterlacing:
574
575 if (is_myth $mpv_path) {
576 $mpv_path = "appending://$mpv_path";
577 $initial_deinterlace = 1;
578 }
579
580 Otherwise, it sets some defaults and loads the file (I forgot what the
581 "dummy" argument is for, but I am sure it is needed by some mpv
582 version):
583
584 $mpv->cmd ("script-message", "osc-visibility", "never", "dummy");
585 $mpv->cmd ("set", "vid", "auto");
586 $mpv->cmd ("set", "aid", "auto");
587 $mpv->cmd ("set", "sid", "no");
588 $mpv->cmd ("set", "file-local-options/chapters-file", $mpv->escape_binary ("$mpv_path.chapters"));
589 $mpv->cmd ("loadfile", $mpv->escape_binary ($mpv_path));
590 $mpv->cmd ("script-message", "osc-visibility", "auto", "dummy");
591
592 Handling events makes the main bulk of video playback code. For example,
593 various ways of ending playback:
594
595 if ($INPUT eq "mpv/quit") { # should not happen, but allows user to kill etc. without consequence
596 $status = 1;
597 mpv_init; # try reinit
598 last;
599
600 } elsif ($INPUT eq "mpv/idle") { # normal end-of-file
601 last;
602
603 } elsif ($INPUT eq "return") {
604 $status = 1;
605 last;
606
607 Or the code that actually starts playback, once the file is loaded:
608
609 our %SAVE_PROPERTY = (aid => 1, sid => 1, "audio-delay" => 1);
610
611 ...
612
613 my $oid = 100;
614
615 } elsif ($INPUT eq "mpv/file-loaded") { # start playing, configure video
616 $mpv->cmd ("seek", $playback_start, "absolute+exact") if $playback_start > 0;
617
618 my $target_fps = eval { $mpv->cmd_recv ("get_property", "container-fps") } || 60;
619 $target_fps *= play_video_speed_mult;
620 set_fps $target_fps;
621
622 unless (eval { $mpv->cmd_recv ("get_property", "video-format") }) {
623 $mpv->cmd ("set", "file-local-options/lavfi-complex", "[aid1] asplit [ao], showcqt=..., format=yuv420p [vo]");
624 };
625
626 for my $prop (keys %SAVE_PROPERTY) {
627 if (exists $PLAYING_STATE->{"mpv_$prop"}) {
628 $mpv->cmd ("set", "$prop", $PLAYING_STATE->{"mpv_$prop"} . "");
629 }
630
631 $mpv->cmd ("observe_property", ++$oid, $prop);
632 }
633
634 play_video_set_speed;
635 $mpv->cmd ("set", "osd-level", "$OSD_LEVEL");
636 $mpv->cmd ("observe_property", ++$oid, "osd-level");
637 $mpv->cmd ("set", "pause", "no");
638
639 $mpv->cmd ("set_property", "deinterlace", "yes")
640 if $initial_deinterlace;
641
642 There is a lot going on here. First it seeks to the actual playback
643 position, if it is not at the start of the file (it would probably be
644 more efficient to set the starting position before loading the file,
645 though, but this is good enough).
646
647 Then it plays with the display fps, to set it to something harmonious
648 w.r.t. the video framerate.
649
650 If the file does not have a video part, it assumes it is an audio file
651 and sets a visualizer.
652
653 Also, a number of properties are not global, but per-file. At the
654 moment, this is "audio-delay", and the current audio/subtitle track,
655 which it sets, and also creates an observer. Again, this doesn't use the
656 observe functionality of this module, but handles it itself, assigning
657 observer ids 100+ to temporary/per-file observers.
658
659 Lastly, it sets some global (or per-youtube-uploader) parameters, such
660 as speed, and unpauses. Property changes are handled like other input
661 events:
662
663 } elsif ($INPUT eq "mpv/property-change") {
664 my $prop = $INPUT_DATA->{name};
665
666 if ($prop eq "chapter-metadata") {
667 if ($INPUT_DATA->{data}{TITLE} =~ /^\[SponsorBlock\]: (.*)/) {
668 my $section = $1;
669 my $skip;
670
671 $skip ||= $SPONSOR_SKIP{$_}
672 for split /\s*,\s*/, $section;
673
674 if (defined $skip) {
675 if ($skip) {
676 # delay a bit, in case we get two metadata changes in quick succession, e.g.
677 # because we have a skip at file load time.
678 $skip_delay = AE::timer 2/50, 0, sub {
679 $mpv->cmd ("no-osd", "add", "chapter", 1);
680 $mpv->cmd ("show-text", "skipped sponsorblock section \"$section\"", 3000);
681 };
682 } else {
683 undef $skip_delay;
684 $mpv->cmd ("show-text", "NOT skipping sponsorblock section \"$section\"", 3000);
685 }
686 } else {
687 $mpv->cmd ("show-text", "UNRECOGNIZED sponsorblock section \"$section\"", 60000);
688 }
689 } else {
690 # cancel a queued skip
691 undef $skip_delay;
692 }
693
694 } elsif (exists $SAVE_PROPERTY{$prop}) {
695 $PLAYING_STATE->{"mpv_$prop"} = $INPUT_DATA->{data};
696 ::state_save;
697 }
698
699 This saves back the per-file properties, and also handles chapter
700 changes in a hacky way.
701
702 Most of the handlers are very simple, though. For example:
703
704 } elsif ($INPUT eq "pause") {
705 $mpv->cmd ("cycle", "pause");
706 $PLAYING_STATE->{curpos} = $mpv->cmd_recv ("get_property", "playback-time");
707 } elsif ($INPUT eq "right") {
708 $mpv->cmd ("osd-msg-bar", "seek", 30, "relative+exact");
709 } elsif ($INPUT eq "left") {
710 $mpv->cmd ("osd-msg-bar", "seek", -5, "relative+exact");
711 } elsif ($INPUT eq "up") {
712 $mpv->cmd ("osd-msg-bar", "seek", +600, "relative+exact");
713 } elsif ($INPUT eq "down") {
714 $mpv->cmd ("osd-msg-bar", "seek", -600, "relative+exact");
715 } elsif ($INPUT eq "select") {
716 $mpv->cmd ("osd-msg-bar", "add", "audio-delay", "-0.100");
717 } elsif ($INPUT eq "start") {
718 $mpv->cmd ("osd-msg-bar", "add", "audio-delay", "0.100");
719 } elsif ($INPUT eq "intfwd") {
720 $mpv->cmd ("no-osd", "frame-step");
721 } elsif ($INPUT eq "audio") {
722 $mpv->cmd ("osd-auto", "cycle", "audio");
723 } elsif ($INPUT eq "subtitle") {
724 $mpv->cmd ("osd-auto", "cycle", "sub");
725 } elsif ($INPUT eq "triangle") {
726 $mpv->cmd ("osd-auto", "cycle", "deinterlace");
727
728 Once a file has finished playing (or the user strops playback), it
729 pauses, unobserves the per-file observers, and saves the current
730 position for to be able to resume:
731
732 $mpv->cmd ("set", "pause", "yes");
733
734 while ($oid > 100) {
735 $mpv->cmd ("unobserve_property", $oid--);
736 }
737
738 $PLAYING_STATE->{curpos} = $mpv->cmd_recv ("get_property", "playback-time");
739
740 And that's most of the mpv-related code.
741
742 Gtk2::CV
743 Gtk2::CV is low-feature image viewer that I use many times daily because
744 it can handle directories with millions of files without falling over.
745 It also had the ability to play videos for ages, but it used an older,
746 crappier protocol to talk to mpv and used ffprobe before playing each
747 file instead of letting mpv handle format/size detection.
748
749 After writing this module, I decided to upgrade Gtk2::CV by making use
750 of it, with the goal of getting rid of ffprobe and being able to reuse
751 mpv processes, which would have a multitude of speed benefits (for
752 example, fork+exec of mpv caused the kernel to close all file
753 descriptors, which could take minutes if a large file was being copied
754 via NFS, as the kernel waited for the buffers to be flushed on close -
755 not having to start mpv gets rid of this issue).
756
757 Setting up is only complicated by the fact that mpv needs to be embedded
758 into an existing window. To keep control of all inputs, Gtk2::CV puts an
759 eventbox in front of mpv, so mpv receives no input events:
760
761 $self->{mpv} = AnyEvent::MPV->new (
762 trace => $ENV{CV_MPV_TRACE},
763 );
764
765 # create an eventbox, so we receive all input events
766 my $box = $self->{mpv_eventbox} = new Gtk2::EventBox;
767 $box->set_above_child (1);
768 $box->set_visible_window (0);
769 $box->set_events ([]);
770 $box->can_focus (0);
771
772 # create a drawingarea that mpv can display into
773 my $window = $self->{mpv_window} = new Gtk2::DrawingArea;
774 $box->add ($window);
775
776 # put the drawingarea intot he eventbox, and the eventbox into our display window
777 $self->add ($box);
778
779 # we need to pass the window id to F<mpv>, which means we need to realise
780 # the drawingarea, so an X window is allocated for it.
781 $self->show_all;
782 $window->realize;
783 my $xid = $window->window->get_xid;
784
785 Then it starts mpv using this setup:
786
787 local $ENV{LC_ALL} = "POSIX";
788 $self->{mpv}->start (
789 "--no-terminal",
790 "--no-input-terminal",
791 "--no-input-default-bindings",
792 "--no-input-cursor",
793 "--input-conf=/dev/null",
794 "--input-vo-keyboard=no",
795
796 "--loop-file=inf",
797 "--force-window=yes",
798 "--idle=yes",
799
800 "--audio-client-name=CV",
801
802 "--osc=yes", # --osc=no displays fading play/pause buttons instead
803
804 "--wid=$xid",
805 );
806
807 $self->{mpv}->cmd ("script-message" => "osc-visibility" => "never", "dummy");
808 $self->{mpv}->cmd ("osc-idlescreen" => "no");
809
810 It also prepares a hack to force a ConfigureNotify event on every video
811 reconfig event:
812
813 # force a configurenotify on every video-reconfig
814 $self->{mpv_reconfig} = $self->{mpv}->register_event (video_reconfig => sub {
815 my ($mpv, $event, $data) = @_;
816
817 $self->mpv_window_update;
818 });
819
820 The way this is done is by doing a "dummy" resize to 1x1 and back:
821
822 $self->{mpv_window}->window->resize (1, 1),
823 $self->{mpv_window}->window->resize ($self->{w}, $self->{h});
824
825 Without this, mpv often doesn't "get" the correct window size. Doing it
826 this way is not nice, but I didn't fine a nicer way to do it.
827
828 When no file is being played, mpv is hidden and prepared:
829
830 $self->{mpv_eventbox}->hide;
831
832 $self->{mpv}->cmd (set_property => "pause" => "yes");
833 $self->{mpv}->cmd ("playlist_remove", "current");
834 $self->{mpv}->cmd (set_property => "video-rotate" => 0);
835 $self->{mpv}->cmd (set_property => "lavfi-complex" => "");
836
837 Loading a file is a bit more complicated, as blu-ray and DVD rips are
838 supported:
839
840 if ($moviedir) {
841 if ($moviedir eq "br") {
842 $mpv->cmd (set => "bluray-device" => $path);
843 $mpv->cmd (loadfile => "bd://");
844 } elsif ($moviedir eq "dvd") {
845 $mpv->cmd (set => "dvd-device" => $path);
846 $mpv->cmd (loadfile => "dvd://");
847 }
848 } elsif ($type eq "video/iso-bluray") {
849 $mpv->cmd (set => "bluray-device" => $path);
850 $mpv->cmd (loadfile => "bd://");
851 } else {
852 $mpv->cmd (loadfile => $mpv->escape_binary ($path));
853 }
854
855 After this, "Gtk2::CV" waits for the file to be loaded, video to be
856 configured, and then queries the video size (to resize its own window)
857 and video format (to decide whether an audio visualizer is needed for
858 audio playback). The problematic word here is "wait", as this needs to
859 be implemented using callbacks.
860
861 This made the code much harder to write, as the whole setup is very
862 asynchronous ("Gtk2::CV" talks to the command interface in mpv, which
863 talks to the decode and playback parts, all of which run asynchronously
864 w.r.t. each other. In practise, this can mean that "Gtk2::CV" waits for
865 a file to be loaded by mpv while the command interface of mpv still
866 deals with the previous file and the decoder still handles an even older
867 file). Adding to this fact is that Gtk2::CV is bound by the glib event
868 loop, which means we cannot wait for replies form mpv anywhere, so
869 everything has to be chained callbacks.
870
871 The way this is handled is by creating a new empty hash ref that is
872 unique for each loaded file, and use it to detect whether the event is
873 old or not, and also store "AnyEvent::MPV" guard objects in it:
874
875 # every time we loaded a file, we create a new hash
876 my $guards = $self->{mpv_guards} = { };
877
878 Then, when we wait for an event to occur, delete the handler, and, if
879 the "mpv_guards" object has changed, we ignore it. Something like this:
880
881 $guards->{file_loaded} = $mpv->register_event (file_loaded => sub {
882 delete $guards->{file_loaded};
883 return if $guards != $self->{mpv_guards};
884
885 Commands do not have guards since they cannot be cancelled, so we don't
886 have to do this for commands. But what prevents us form misinterpreting
887 an old event? Since mpv (by default) handles commands synchronously, we
888 can queue a dummy command, whose only purpose is to tell us when all
889 previous commands are done. We use "get_version" for this.
890
891 The simplified code looks like this:
892
893 Scalar::Util::weaken $self;
894
895 $mpv->cmd ("get_version")->cb (sub {
896
897 $guards->{file_loaded} = $mpv->register_event (file_loaded => sub {
898 delete $guards->{file_loaded};
899 return if $guards != $self->{mpv_guards};
900
901 $mpv->cmd (get_property => "video-format")->cb (sub {
902 return if $guards != $self->{mpv_guards};
903
904 # video-format handling
905 return if eval { $_[0]->recv; 1 };
906
907 # no video? assume audio and visualize, cpu usage be damned
908 $mpv->cmd (set => "lavfi-complex" => ...");
909 });
910
911 $guards->{show} = $mpv->register_event (video_reconfig => sub {
912 delete $guards->{show};
913 return if $guards != $self->{mpv_guards};
914
915 $self->{mpv_eventbox}->show_all;
916
917 $w = $mpv->cmd (get_property => "dwidth");
918 $h = $mpv->cmd (get_property => "dheight");
919
920 $h->cb (sub {
921 $w = eval { $w->recv };
922 $h = eval { $h->recv };
923
924 $mpv->cmd (set_property => "pause" => "no");
925
926 if ($w && $h) {
927 # resize our window
928 }
929
930 });
931 });
932
933 });
934
935 });
936
937 Most of the rest of the code is much simpler and just deals with
938 forwarding user commands:
939
940 } elsif ($key == $Gtk2::Gdk::Keysyms{Right}) { $mpv->cmd ("osd-msg-bar" => seek => "+10");
941 } elsif ($key == $Gtk2::Gdk::Keysyms{Left} ) { $mpv->cmd ("osd-msg-bar" => seek => "-10");
942 } elsif ($key == $Gtk2::Gdk::Keysyms{Up} ) { $mpv->cmd ("osd-msg-bar" => seek => "+60");
943 } elsif ($key == $Gtk2::Gdk::Keysyms{Down} ) { $mpv->cmd ("osd-msg-bar" => seek => "-60");
944 } elsif ($key == $Gtk2::Gdk::Keysyms{a}) ) { $mpv->cmd ("osd-msg-msg" => cycle => "audio");
945 } elsif ($key == $Gtk2::Gdk::Keysyms{j} ) { $mpv->cmd ("osd-msg-msg" => cycle => "sub");
946 } elsif ($key == $Gtk2::Gdk::Keysyms{o} ) { $mpv->cmd ("no-osd" => "cycle-values", "osd-level", "2", "3", "0", "2");
947 } elsif ($key == $Gtk2::Gdk::Keysyms{p} ) { $mpv->cmd ("no-osd" => cycle => "pause");
948 } elsif ($key == $Gtk2::Gdk::Keysyms{9} ) { $mpv->cmd ("osd-msg-bar" => add => "ao-volume", "-2");
949 } elsif ($key == $Gtk2::Gdk::Keysyms{0} ) { $mpv->cmd ("osd-msg-bar" => add => "ao-volume", "+2");
385 950
386SEE ALSO 951SEE ALSO
387 AnyEvent, the mpv command documentation 952 AnyEvent, the mpv command documentation
388 <https://mpv.io/manual/stable/#command-interface>. 953 <https://mpv.io/manual/stable/#command-interface>.
389 954

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines