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

Comparing AnyEvent-MPV/MPV.pm (file contents):
Revision 1.5 by root, Sun Mar 19 23:13:25 2023 UTC vs.
Revision 1.12 by root, Mon Mar 20 13:44:40 2023 UTC

62 $quit->recv; 62 $quit->recv;
63 63
64This starts F<mpv> with the two arguments C<--> and C<$videofile>, which 64This starts F<mpv> with the two arguments C<--> and C<$videofile>, which
65it should load and play. It then waits two seconds by starting a timer and 65it should load and play. It then waits two seconds by starting a timer and
66quits. The C<trace> argument to the constructor makes F<mpv> more verbose 66quits. The C<trace> argument to the constructor makes F<mpv> more verbose
67and also prints the commands and responses, so you cna have an idea what 67and also prints the commands and responses, so you can have an idea what
68is going on. 68is going on.
69
70In 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"}
69 83
70This is not usually very useful (you could just run F<mpv> as a simple 84This is not usually very useful (you could just run F<mpv> as a simple
71shell command), so let us load the file at runtime: 85shell command), so let us load the file at runtime:
72 86
73 use AnyEvent; 87 use AnyEvent;
133 my $quit = AE::cv; 147 my $quit = AE::cv;
134 148
135 my $mpv = AnyEvent::MPV->new ( 149 my $mpv = AnyEvent::MPV->new (
136 trace => 1, 150 trace => 1,
137 args => ["--pause", "--idle=yes"], 151 args => ["--pause", "--idle=yes"],
138 on_event => sub {
139 my ($mpv, $event, $data) = @_;
140
141 if ($event eq "start-file") {
142 $mpv->cmd ("set", "pause", "no");
143 } elsif ($event eq "end-file") {
144 print "end-file<$data->{reason}>\n";
145 $quit->send;
146 }
147 },
148 ); 152 );
149 153
150 $mpv->start; 154 $mpv->start;
155
156 $mpv->register_event (start_file => sub {
157 $mpv->cmd ("set", "pause", "no");
158 });
159
160 $mpv->register_event (end_file => sub {
161 my ($mpv, $event, $data) = @_;
162
163 print "end-file<$data->{reason}>\n";
164 $quit->send;
165 });
166
151 $mpv->cmd (loadfile => $mpv->escape_binary ($videofile)); 167 $mpv->cmd (loadfile => $mpv->escape_binary ($videofile));
152 168
153 $quit->recv; 169 $quit->recv;
154 170
155This example uses a global condvar C<$quit> to wait for the file to finish 171This example uses a global condvar C<$quit> to wait for the file to finish
156playing. Also, most of the logic is now in an C<on_event> callback, which 172playing. Also, most of the logic is now implement in event handlers.
157receives an event name and the actual event object.
158 173
159The two events we handle are C<start-file>, which is emitted by F<mpv> 174The two events handlers we register are C<start-file>, which is emitted by
160once it has loaded a new file, and C<end-file>, which signals the end 175F<mpv> once it has loaded a new file, and C<end-file>, which signals the
161of a file. 176end of a file (underscores are internally replaced by minus signs, so you
177cna speicfy event names with either).
162 178
163In the former event, we again set the C<pause> property to C<no> so the 179In the C<start-file> event, we again set the C<pause> property to C<no>
164movie starts playing. For the latter event, we tell the main program to 180so the movie starts playing. For the C<end-file> event, we tell the main
165quit by invoking C<$quit>. 181program to quit by invoking C<$quit>.
166 182
167This should conclude the basics of operation. There are a few more 183This should conclude the basics of operation. There are a few more
168examples later in the documentation. 184examples later in the documentation.
169 185
170=head2 ENCODING CONVENTIONS 186=head2 ENCODING CONVENTIONS
195use Scalar::Util (); 211use Scalar::Util ();
196 212
197use AnyEvent (); 213use AnyEvent ();
198use AnyEvent::Util (); 214use AnyEvent::Util ();
199 215
216our $VERSION = '0.2';
217
218sub OBSID() { 0x10000000000000 } # 2**52
219
200our $JSON = eval { require JSON::XS; JSON::XS:: } 220our $JSON = eval { require JSON::XS; JSON::XS:: }
201 || do { require JSON::PP; JSON::PP:: }; 221 || do { require JSON::PP; JSON::PP:: };
202 222
203our $JSON_CODER = 223our $JSON_CODER =
204
205our $VERSION = '0.1';
206 224
207our $mpv_path; # last mpv path used 225our $mpv_path; # last mpv path used
208our $mpv_optionlist; # output of mpv --list-options 226our $mpv_optionlist; # output of mpv --list-options
209 227
210=item $mpv = AnyEvent::MPV->new (key => value...) 228=item $mpv = AnyEvent::MPV->new (key => value...)
372 my $trace = delete $self->{trace} || sub { }; 390 my $trace = delete $self->{trace} || sub { };
373 391
374 $trace = sub { warn "$_[0] $_[1]\n" } if $trace && !ref $trace; 392 $trace = sub { warn "$_[0] $_[1]\n" } if $trace && !ref $trace;
375 393
376 my $buf; 394 my $buf;
377 my $wbuf;
378 395
379 Scalar::Util::weaken $self; 396 Scalar::Util::weaken $self;
380 397
381 $self->{rw} = AE::io $fh, 0, sub { 398 $self->{rw} = AE::io $fh, 0, sub {
382 if (sysread $fh, $buf, 8192, length $buf) { 399 if (sysread $fh, $buf, 8192, length $buf) {
385 402
386 if ("{" eq substr $1, 0, 1) { 403 if ("{" eq substr $1, 0, 1) {
387 eval { 404 eval {
388 my $reply = $JSON->new->latin1->decode ($1); 405 my $reply = $JSON->new->latin1->decode ($1);
389 406
390 if (exists $reply->{event}) { 407 if (defined (my $event = delete $reply->{event})) {
391 if ( 408 if (
392 $reply->{event} eq "client-message" 409 $event eq "client-message"
393 and $reply->{args}[0] eq "AnyEvent::MPV" 410 and $reply->{args}[0] eq "AnyEvent::MPV"
394 ) { 411 ) {
395 if ($reply->{args}[1] eq "key") { 412 if ($reply->{args}[1] eq "key") {
396 (my $key = $reply->{args}[2]) =~ s/\\x(..)/chr hex $1/ge; 413 (my $key = $reply->{args}[2]) =~ s/\\x(..)/chr hex $1/ge;
397 $self->on_key ($key); 414 $self->on_key ($key);
398 } 415 }
416 } elsif (
417 $event eq "property-change"
418 and OBSID <= $reply->{id}
419 ) {
420 if (my $cb = $self->{obscb}{$reply->{id}}) {
421 $cb->($self, $event, $reply->{data});
422 }
399 } else { 423 } else {
424 if (my $cbs = $self->{evtcb}{$event}) {
425 for my $evtid (keys %$cbs) {
426 my $cb = $cbs->{$evtid}
427 or next;
428 $cb->($self, $event, $reply);
429 }
430 }
431
400 $self->on_event ($reply->{event}, $reply); 432 $self->on_event ($event, $reply);
401 } 433 }
402 } elsif (exists $reply->{request_id}) { 434 } elsif (exists $reply->{request_id}) {
403 my $cv = delete $self->{cmd_cv}{$reply->{request_id}}; 435 my $cv = delete $self->{cmdcv}{$reply->{request_id}};
404 436
405 unless ($cv) { 437 unless ($cv) {
406 warn "no cv found for request id <$reply->{request_id}>\n"; 438 warn "no cv found for request id <$reply->{request_id}>\n";
407 next; 439 next;
408 } 440 }
428 $self->stop; 460 $self->stop;
429 $self->on_eof; 461 $self->on_eof;
430 } 462 }
431 }; 463 };
432 464
465 my $wbuf;
466 my $reqid;
467
433 $self->{_send} = sub { 468 $self->{_cmd} = sub {
434 $wbuf .= "$_[0]\n"; 469 my $cv = AE::cv;
435 470
471 $self->{cmdcv}{++$reqid} = $cv;
472
473 my $cmd = $JSON->new->utf8->encode ({ command => ref $_[0] ? $_[0] : \@_, request_id => $reqid*1 });
474
475 # (un-)apply escape_binary hack
476 $cmd =~ s/\xf4\x8e\x97\x9f(..)/sprintf sprintf "\\x%02x", hex $1/ges; # f48e979f == 10e5df in utf-8
477
436 $trace->(">mpv" => "$_[0]"); 478 $trace->(">mpv" => $cmd);
479
480 $wbuf .= "$cmd\n";
437 481
438 $self->{ww} ||= AE::io $fh, 1, sub { 482 $self->{ww} ||= AE::io $fh, 1, sub {
439 my $len = syswrite $fh, $wbuf; 483 my $len = syswrite $fh, $wbuf;
440 substr $wbuf, 0, $len, ""; 484 substr $wbuf, 0, $len, "";
441 undef $self->{ww} unless length $wbuf; 485 undef $self->{ww} unless length $wbuf;
442 }; 486 };
487
488 $cv
443 }; 489 };
444 490
445 1 491 1
492}
493
494sub DESTROY {
495 $_[0]->stop;
446} 496}
447 497
448=item $mpv->stop 498=item $mpv->stop
449 499
450Ensures that F<mpv> is being stopped, by killing F<mpv> with a C<TERM> 500Ensures that F<mpv> is being stopped, by killing F<mpv> with a C<TERM>
465 kill TERM => $self->{pid}; 515 kill TERM => $self->{pid};
466 516
467 } 517 }
468 518
469 delete $self->{pid}; 519 delete $self->{pid};
470 delete $self->{cmd_cv}; 520 delete $self->{cmdcv};
521 delete $self->{evtid};
522 delete $self->{evtcb};
523 delete $self->{obsid};
524 delete $self->{obscb};
525 delete $self->{wbuf};
471} 526}
472 527
473=item $mpv->on_eof 528=item $mpv->on_eof
474 529
475This method is called when F<mpv> quits - usually unexpectedly. The 530This method is called when F<mpv> quits - usually unexpectedly. The
490 545
491This method is called when F<mpv> sends an asynchronous event. The default 546This method is called when F<mpv> sends an asynchronous event. The default
492implementation will call the C<on_event> code reference specified in the 547implementation will call the C<on_event> code reference specified in the
493constructor, or do nothing if none was given. 548constructor, or do nothing if none was given.
494 549
495The first/implicit argument is the C<$mpv> object, the second is the event 550The first/implicit argument is the C<$mpv> object, the second is the
496name (same as C<< $data->{event} >>, purely for convenience), and the 551event name (same as C<< $data->{event} >>, purely for convenience), and
497third argument is the full event object as sent by F<mpv>. See L<List of 552the third argument is the event object as sent by F<mpv> (sans C<event>
498events|https://mpv.io/manual/stable/#list-of-events> in its documentation. 553key). See L<List of events|https://mpv.io/manual/stable/#list-of-events>
554in its documentation.
499 555
500For subclassing, see I<SUBCLASSING>, below. 556For subclassing, see I<SUBCLASSING>, below.
501 557
502=cut 558=cut
503 559
562On error, the condvar will croak when C<recv> is called. 618On error, the condvar will croak when C<recv> is called.
563 619
564=cut 620=cut
565 621
566sub cmd { 622sub cmd {
567 my ($self, @cmd) = @_; 623 my $self = shift;
568 624
569 my $cv = AE::cv; 625 $self->{_cmd}->(@_)
570
571 my $reqid = ++$self->{reqid};
572 $self->{cmd_cv}{$reqid} = $cv;
573
574 my $cmd = $JSON->new->utf8->encode ({ command => ref $cmd[0] ? $cmd[0] : \@cmd, request_id => $reqid*1 });
575
576 # (un-)apply escape_binary hack
577 $cmd =~ s/\xf4\x8e\x97\x9f(..)/sprintf sprintf "\\x%02x", hex $1/ges; # f48e979f == 10e5df in utf-8
578
579 $self->{_send}($cmd);
580
581 $cv
582} 626}
583 627
584=item $result = $mpv->cmd_recv ($command => $arg, $arg...) 628=item $result = $mpv->cmd_recv ($command => $arg, $arg...)
585 629
586The same as calling C<cmd> and immediately C<recv> on its return 630The same as calling C<cmd> and immediately C<recv> on its return
596 &cmd->recv 640 &cmd->recv
597} 641}
598 642
599=item $mpv->bind_key ($INPUT => $string) 643=item $mpv->bind_key ($INPUT => $string)
600 644
601This is an extension implement by this module to make it easy to get key events. The way this is implemented 645This is an extension implement by this module to make it easy to get key
602is to bind a C<client-message> witha first argument of C<AnyEvent::MPV> and the C<$string> you passed. This C<$string> is then 646events. The way this is implemented is to bind a C<client-message> witha
603passed ot the C<on_key> handle when the key is proessed, e.g.: 647first argument of C<AnyEvent::MPV> and the C<$string> you passed. This
648C<$string> is then passed to the C<on_key> handle when the key is
649proessed, e.g.:
604 650
605 my $mpv = AnyEvent::MPV->new ( 651 my $mpv = AnyEvent::MPV->new (
606 on_key => sub { 652 on_key => sub {
607 my ($mpv, $key) = @_; 653 my ($mpv, $key) = @_;
608 654
612 }, 658 },
613 ); 659 );
614 660
615 $mpv_>bind_key (ESC => "letmeout"); 661 $mpv_>bind_key (ESC => "letmeout");
616 662
663You cna find a list of key names L<in the mpv
664documentation|https://mpv.io/manual/stable/#key-names>.
665
617The key configuration is lost when F<mpv> is stopped and must be (re-)done 666The key configuration is lost when F<mpv> is stopped and must be (re-)done
618after every C<start>. 667after every C<start>.
619 668
620=cut 669=cut
621 670
622sub bind_key { 671sub bind_key {
623 my ($self, $key, $event) = @_; 672 my ($self, $key, $event) = @_;
624 673
625 $event =~ s/([^A-Za-z0-9\-_])/sprintf "\\x%02x", ord $1/ge; 674 $event =~ s/([^A-Za-z0-9\-_])/sprintf "\\x%02x", ord $1/ge;
626 $self->cmd (keybind => $key => "no-osd script-message AnyEvent::MPV key $event"); 675 $self->cmd (keybind => $key => "no-osd script-message AnyEvent::MPV key $event");
676}
677
678=item [$guard] = $mpv->register_event ($event => $coderef->($mpv, $event, $data))
679
680This method registers a callback to be invoked for a specific
681event. Whenever the event occurs, it calls the coderef with the C<$mpv>
682object, the C<$event> name and the event object, just like the C<on_event>
683method.
684
685For a lst of events, see L<the mpv
686documentation|https://mpv.io/manual/stable/#list-of-events>. Any
687underscore in the event name is replaced by a minus sign, so you can
688specify event names using underscores for easier quoting in Perl.
689
690In void context, the handler stays registered until C<stop> is called. In
691any other context, it returns a guard object that, when destroyed, will
692unregister the handler.
693
694You can register multiple handlers for the same event, and this method
695does not interfere with the C<on_event> mechanism. That is, you can
696completely ignore this method and handle events in a C<on_event> handler,
697or mix both approaches as you see fit.
698
699=cut
700
701sub AnyEvent::MPV::Unevent::DESTROY {
702 my ($evtcb, $event, $evtid) = @{$_[0]};
703 delete $evtcb->{$event}{$evtid};
704}
705
706sub register_event {
707 my ($self, $event, $cb) = @_;
708
709 $event =~ y/_/-/;
710
711 my $evtid = ++$self->{evtid};
712 $self->{evtcb}{$event}{$evtid} = $cb;
713
714 defined wantarray
715 and bless [$self->{evtcb}, $event, $evtid], AnyEvent::MPV::Unevent::
716}
717
718=item [$guard] = $mpv->observe_property ($name => $coderef->($mpv, $name, $value))
719
720=item [$guard] = $mpv->observe_property_string ($name => $coderef->($mpv, $name, $value))
721
722These methods wrap a registry system around F<mpv>'s C<observe_property>
723and C<observe_property_string> commands - every time the named property
724changes, the coderef is invoked with the C<$mpv> object, the name of the
725property and the new value.
726
727For a list of properties that you can observe, see L<the mpv
728documentation|https://mpv.io/manual/stable/#property-list>.
729
730Due to the (sane :) way F<mpv> handles these requests, you will always
731get a property cxhange event right after registering an observer (meaning
732you don't have to query the current value), and it is also possible to
733register multiple observers for the same property - they will all be
734handled properly.
735
736When called in void context, the observer stays in place until F<mpv>
737is stopped. In any otrher context, these methods return a guard
738object that, when it goes out of scope, unregisters the observe using
739C<unobserve_property>.
740
741Internally, this method uses observer ids of 2**52 (0x10000000000000) or
742higher - it will not interfere with lower ovserver ids, so it is possible
743to completely ignore this system and execute C<observe_property> commands
744yourself, whilst listening to C<property-change> events - as long as your
745ids stay below 2**52.
746
747Example: register observers for changtes in C<aid> and C<sid>. Note that
748a dummy statement is added to make sure the method is called in void
749context.
750
751 sub register_observers {
752 my ($mpv) = @_;
753
754 $mpv->observe_property (aid => sub {
755 my ($mpv, $name, $value) = @_;
756 print "property aid (=$name) has changed to $value\n";
757 });
758
759 $mpv->observe_property (sid => sub {
760 my ($mpv, $name, $value) = @_;
761 print "property sid (=$name) has changed to $value\n";
762 });
763
764 () # ensure the above method is called in void context
765 }
766
767=cut
768
769sub AnyEvent::MPV::Unobserve::DESTROY {
770 my ($mpv, $obscb, $obsid) = @{$_[0]};
771
772 delete $obscb->{$obsid};
773
774 if ($obscb == $mpv->{obscb}) {
775 $mpv->cmd (unobserve_property => $obsid+0);
776 }
777}
778
779sub _observe_property {
780 my ($self, $type, $property, $cb) = @_;
781
782 my $obsid = OBSID + ++$self->{obsid};
783 $self->cmd ($type => $obsid+0, $property);
784 $self->{obscb}{$obsid} = $cb;
785
786 defined wantarray and do {
787 my $unobserve = bless [$self, $self->{obscb}, $obsid], AnyEvent::MPV::Unobserve::;
788 Scalar::Util::weaken $unobserve->[0];
789 $unobserve
790 }
791}
792
793sub observe_property {
794 my ($self, $property, $cb) = @_;
795
796 $self->_observe_property (observe_property => $property, $cb)
797}
798
799sub observe_property_string {
800 my ($self, $property, $cb) = @_;
801
802 $self->_observe_property (observe_property_string => $property, $cb)
627} 803}
628 804
629=back 805=back
630 806
631=head2 SUBCLASSING 807=head2 SUBCLASSING

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines