PageRenderTime 52ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/gmusicbrowser-1.1.9/gmusicbrowser_mplayer.pm

#
Perl | 209 lines | 169 code | 23 blank | 17 comment | 26 complexity | 4d81d9a9125650ccaeafc5c83cf7abfb MD5 | raw file
Possible License(s): GPL-3.0
  1. # Copyright (C) 2005-2010 Quentin Sculo <squentin@free.fr>
  2. #
  3. # This file is part of Gmusicbrowser.
  4. # Gmusicbrowser is free software; you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License version 3, as
  6. # published by the Free Software Foundation
  7. package Play_mplayer;
  8. use strict;
  9. use warnings;
  10. use IO::Handle;
  11. use POSIX ':sys_wait_h'; #for WNOHANG in waitpid
  12. #$SIG{CHLD} = 'IGNORE'; # to make sure there are no zombies #cause crash after displaying a file dialog and then runnning an external command with mandriva's gtk2
  13. #$SIG{CHLD} = sub { while (waitpid(-1, WNOHANG)>0) {} };
  14. my (@cmd_and_args,$file,$ChildPID,$WatchTag,$WatchTag2,$OUTPUTfh,@pidToKill,$Kill9);
  15. my $CMDfh;
  16. my (%supported,$mplayer);
  17. my $SoftVolume;
  18. $::PlayPacks{Play_mplayer}=1; #register the package
  19. sub init
  20. { undef %supported;
  21. $mplayer= $::Options{mplayer_cmd};
  22. if ($mplayer && !-x $mplayer && !(::first { -x $_ } map $_.::SLASH.$mplayer, split /:/, $ENV{PATH}))
  23. { $mplayer=undef;
  24. }
  25. $mplayer ||= ::first { -x $_ } map $_.::SLASH.'mplayer', split /:/, $ENV{PATH};
  26. return unless $mplayer;
  27. return bless {},__PACKAGE__;
  28. }
  29. sub supported_formats
  30. { return () unless $mplayer;
  31. unless (keys %supported)
  32. {for (qx($mplayer -msglevel all=4 -ac help))
  33. { if (m/^(?:mad|ffmp3)\W.*working/){$supported{mp3}=undef}
  34. elsif (m/^vorbis.*working/) {$supported{oga}=undef}
  35. elsif (m/^musepack.*working/) {$supported{mpc}=undef}
  36. elsif (m/^ffflac.*working/) {$supported{flac}=undef}
  37. elsif (m/^ffwavpack.*working/){$supported{wv}=undef}
  38. elsif (m/^ffape.*working/) {$supported{ape}=undef}
  39. elsif (m/^faad.*working/) {$supported{m4a}=undef}
  40. }
  41. }
  42. return keys %supported;
  43. }
  44. sub VolInit
  45. { # check if support -volume option
  46. $SoftVolume= !system($mplayer,qw/-really-quiet -softvol -volume 100/) unless defined $SoftVolume;
  47. return undef if $SoftVolume; #use methods from this package
  48. return Play_amixer::init(); #use methods from Play_amixer
  49. }
  50. sub Play
  51. { (undef,$file,my$sec)=@_;
  52. &Stop if $ChildPID;
  53. #if ($ChildPID) { print $CMDfh "loadfile $file\n"; print $CMDfh "seek $sec 2\n" if $sec; return}
  54. @cmd_and_args=($mplayer,qw/-nocache -slave -vo null -nolirc/);
  55. push @cmd_and_args, qw/-softvol -volume/, cubicvolume($::Volume) if $SoftVolume;
  56. warn "@cmd_and_args\n" if $::debug;
  57. #push @cmd_and_args,$device_option,$device unless $device eq 'default';
  58. push @cmd_and_args,split / /,$::Options{mplayeroptions} if $::Options{mplayeroptions};
  59. push @cmd_and_args,'-ss',$sec if $sec;
  60. push @cmd_and_args,'-ac','ffwavpack' if $file=~m/\.wvc?$/;
  61. push @cmd_and_args,'-ac','ffape' if $file=~m/\.ape$/;
  62. push @cmd_and_args, '--',$file;
  63. pipe $OUTPUTfh,my$wfh;
  64. pipe my($rfh),$CMDfh;
  65. $ChildPID=fork;
  66. if (!defined $ChildPID) { warn "gmusicbrowser_mplayer : fork failed : $!\n"; ::ErrorPlay("Fork failed : $!"); return }
  67. elsif ($ChildPID==0) #child
  68. { close $OUTPUTfh; close $CMDfh;
  69. open my($olderr), ">&", \*STDERR;
  70. open \*STDIN, '<&='.fileno $rfh;
  71. open \*STDOUT,'>&='.fileno $wfh;
  72. open \*STDERR,'>&='.fileno $wfh;
  73. exec @cmd_and_args or print $olderr "launch failed (@cmd_and_args) : $!\n";
  74. POSIX::_exit(1);
  75. }
  76. close $wfh; close $rfh;
  77. $CMDfh->autoflush(1);
  78. #print $CMDfh "LOAD $file\n";
  79. #SkipTo(undef,$sec) if $sec;
  80. $OUTPUTfh->blocking(0); #set non-blocking IO
  81. warn "playing $file (pid=$ChildPID)\n";
  82. $WatchTag= Glib::IO->add_watch(fileno($OUTPUTfh),'hup',\&_eos_cb);
  83. $WatchTag2=Glib::IO->add_watch(fileno($OUTPUTfh),'in',\&_remotemsg);
  84. #Glib::Timeout->add(500,\&_UpdateTime);
  85. }
  86. sub _eos_cb
  87. { #close $OUTPUTfh;
  88. while (waitpid(-1, WNOHANG)>0) {} #reap dead children
  89. Glib::Source->remove($WatchTag);
  90. Glib::Source->remove($WatchTag2);
  91. $WatchTag=$WatchTag2=$ChildPID=undef;
  92. ::end_of_file;
  93. return 1;
  94. }
  95. sub _remotemsg
  96. { my $buf;
  97. my @line=(<$OUTPUTfh>);
  98. my $line=pop @line; #only read the last line
  99. chomp $line;
  100. if ($line=~m/^A:\s*(\d+).\d /)
  101. { ::UpdateTime( $1 );
  102. }
  103. elsif ($::debug) {warn "mplayer:$_\n" for @line,$line}
  104. return 1;
  105. }
  106. sub Pause
  107. { print $CMDfh "pause\n";
  108. }
  109. sub Resume
  110. { print $CMDfh "pause\n";
  111. }
  112. sub SkipTo
  113. { my $sec=$_[1];
  114. ::setlocale(::LC_NUMERIC, 'C');
  115. print $CMDfh "pausing_keep seek $sec 2\n";
  116. ::setlocale(::LC_NUMERIC, '');
  117. }
  118. sub Stop
  119. { if ($WatchTag)
  120. { Glib::Source->remove($WatchTag);
  121. Glib::Source->remove($WatchTag2);
  122. $WatchTag=$WatchTag2=undef;
  123. }
  124. if ($ChildPID)
  125. { print $CMDfh "quit\n";
  126. #close $OUTPUTfh;
  127. Glib::Timeout->add( 100,\&_Kill_timeout ) unless @pidToKill;
  128. $Kill9=0; #_Kill_timeout will first try INT, then KILL
  129. push @pidToKill,$ChildPID;
  130. undef $ChildPID;
  131. }
  132. }
  133. sub _Kill_timeout #make sure old children are dead
  134. { while (waitpid(-1, WNOHANG)>0) {} #reap dead children
  135. @pidToKill=grep kill(0,$_), @pidToKill; #checks to see which ones are still there
  136. if (@pidToKill)
  137. { warn "Sending ".($Kill9 ? 'KILL' : 'INT')." signal to @pidToKill\n" if $::debug;
  138. if ($Kill9) {kill KILL=>@pidToKill;}
  139. else {kill INT=>@pidToKill;}
  140. $Kill9=1; #use KILL if they are still there next time
  141. }
  142. return @pidToKill; #removes the timeout if no more @pidToKill
  143. }
  144. sub error
  145. { ::ErrorPlay(join(' ',@cmd_and_args)." :\n".$_[0]);
  146. }
  147. sub AdvancedOptions
  148. { my $vbox=Gtk2::VBox->new(::FALSE, 2);
  149. my $sg1=Gtk2::SizeGroup->new('horizontal');
  150. my $opt=::NewPrefEntry('mplayeroptions',_"mplayer options :", sizeg1=>$sg1);
  151. my $cmd=::NewPrefEntry('mplayer_cmd',_"mplayer executable :", cb=> \&init, tip=>_"Will use default if not found", sizeg1=>$sg1);
  152. $vbox->pack_start($_,::FALSE,::FALSE,2), for $cmd,$opt;
  153. VolInit() unless defined $SoftVolume;
  154. $vbox->pack_start(Play_amixer::make_option_widget(),::FALSE,::FALSE,2) unless $SoftVolume;
  155. return $vbox;
  156. }
  157. # Volume functions
  158. sub GetVolume {$::Volume}
  159. sub GetMute {$::Mute}
  160. sub SetVolume
  161. { shift;
  162. my $set=shift;
  163. if ($set eq 'mute') { $::Mute=$::Volume; $::Volume=0; }
  164. elsif ($set eq 'unmute') { $::Volume=$::Mute; $::Mute=0; }
  165. elsif ($set=~m/^\+(\d+)$/) { $::Volume+=$1; }
  166. elsif ($set=~m/^-(\d+)$/) { $::Volume-=$1; }
  167. elsif ($set=~m/(\d+)/) { $::Volume =$1; }
  168. $::Volume=0 if $::Volume<0;
  169. $::Volume=100 if $::Volume>100;
  170. my $cubicvol= cubicvolume($::Volume); #use a cubic volume scale
  171. print $CMDfh "volume $cubicvol 1\n" if $ChildPID;
  172. ::HasChanged('Vol');
  173. $::Options{Volume}=$::Volume;
  174. $::Options{Volume_mute}=$::Mute;
  175. }
  176. sub cubicvolume #convert a linear volume to cubic volume scale
  177. { my $vol=$_[0];
  178. $vol= 100*($vol/100)**3;
  179. # will be sent to mplayer as string, make sure it use a dot as decimal separator
  180. ::setlocale(::LC_NUMERIC, 'C');
  181. $vol="$vol";
  182. ::setlocale(::LC_NUMERIC, '');
  183. return $vol;
  184. }
  185. #sub sendcmd {print $CMDfh "$_[0]\n";} #DEBUG #Play_mplayer::sendcmd('volume 0')
  186. 1;