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

Comparing AnyEvent-MPV/README (file contents):
Revision 1.3 by root, Mon Mar 20 12:31:31 2023 UTC vs.
Revision 1.4 by root, Wed Mar 22 19:37:00 2023 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 );
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 cna speicfy 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,
366 }, 377 },
367 ); 378 );
368 379
369 $mpv_>bind_key (ESC => "letmeout"); 380 $mpv_>bind_key (ESC => "letmeout");
370 381
382 You cna 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 lst 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.
373 415
374 [$guard] = $mpv->observe_property ($name => $coderef->($mpv, $name, 416 [$guard] = $mpv->observe_property ($name => $coderef->($mpv, $name,
375 $value)) 417 $value))
376 [$guard] = $mpv->observe_property_string ($name => $coderef->($mpv, 418 [$guard] = $mpv->observe_property_string ($name => $coderef->($mpv,
377 $name, $value)) 419 $name, $value))
430 simply not care and deal with the breakage. 472 simply not care and deal with the breakage.
431 473
432 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
433 can also specify all event handlers as constructor keys. 475 can also specify all event handlers as constructor keys.
434 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 diea 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 environemnt, 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 vidoe
553 chapters change. Preadting "AnyEvent::MPV", it handles observers
554 manually:
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 n ot 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 probaby 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 obsevrer 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 thats 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 upgprade Gtk2::CV by making use
750 of it, with the goal of getting rid of ffprobe and being ablew 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 thr 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 vidoe
811 reconfig:
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 bluray 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 imploemented 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 cnanot 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");
950
435SEE ALSO 951SEE ALSO
436 AnyEvent, the mpv command documentation 952 AnyEvent, the mpv command documentation
437 <https://mpv.io/manual/stable/#command-interface>. 953 <https://mpv.io/manual/stable/#command-interface>.
438 954
439AUTHOR 955AUTHOR

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines