You've already forked lazarus-ccr
reading unicode tags and filenames, show/hide app window with single click tray icon, create album cover if not exists, several fixes when there is just one song in collection, artist tree icons, handling of track numbers in n[/m] format, show path of current file (in playlist and title list), log output was corrupt due the use of crt unit, etc. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@1761 8e941d3f-bd1b-0410-a28a-d453659cc2b4
468 lines
13 KiB
ObjectPascal
468 lines
13 KiB
ObjectPascal
|
|
{
|
|
Mplayer backend for Cactus Jukebox
|
|
|
|
written by Sebastian Kraft, <c> 2006-2008
|
|
|
|
Contact the author at: sebastian_kraft@gmx.de
|
|
|
|
This Software is published under the GPL
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
unit mplayer;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, playerclass, process, debug, functions;
|
|
|
|
type
|
|
|
|
{ TMPlayerClass }
|
|
|
|
TMPlayerClass = class(TPlayerClass)
|
|
Private
|
|
FMPlayerPath: string;
|
|
MPlayerProcess: TProcess;
|
|
FLastGet_Pos: integer;
|
|
procedure SendCommand(cmd:string);
|
|
function GetProcessOutput:string;
|
|
function GetMPlayerPlaying: boolean;
|
|
Public
|
|
ExternalConfigFile: string;
|
|
UseExternalConfig: boolean;
|
|
|
|
constructor create; override;
|
|
destructor destroy;
|
|
|
|
function play(index:integer):byte;override;
|
|
function play(url: string):byte;override;
|
|
procedure pause;override;
|
|
procedure stop;override;
|
|
function next_track:byte;override;
|
|
function prev_track:byte;override;
|
|
|
|
function Get_Stream_Status:TStreamStatus;override;
|
|
|
|
function Get_TrackLength:longint;override;
|
|
function Get_Time:longint;override;
|
|
function Get_TimeStr:string;override;
|
|
function Get_TimeRemainingStr: string; override;
|
|
|
|
function Get_FilePosition:longint;override;
|
|
function get_FileLength:longint;override;
|
|
|
|
procedure Set_Time(ms: longint);override;
|
|
procedure Set_FilePosition(fpos:longint);override;
|
|
procedure Set_Volume(vol:byte);override;
|
|
|
|
procedure Mute;override;
|
|
function Muted:boolean;override;
|
|
|
|
function setMplayerBinaryDir(dir: string):boolean;
|
|
|
|
property MPlayerPath: string read FMPlayerPath;
|
|
property playing: boolean read GetMPlayerPlaying;
|
|
|
|
end;
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
var mplayerobj: TMPlayerClass;
|
|
|
|
implementation
|
|
|
|
uses math; //used for logarithmic volume calculation
|
|
|
|
{$ifdef linux}
|
|
const MPLAYER_BINARY='mplayer';
|
|
{$endif}
|
|
{$ifdef windows}
|
|
const MPLAYER_BINARY='mplayer.exe';
|
|
{$endif}
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
{ TMPlayerClass }
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
procedure TMPlayerClass.SendCommand(cmd: string);
|
|
begin
|
|
// writeln('sendcommand');
|
|
cmd:=cmd+#10; //MPLayer always needs #10 as Lineending, no matter if win32 or linux
|
|
try
|
|
// writeln('sendcommand2');
|
|
if GetMPlayerPlaying then MPlayerProcess.Input.write(cmd[1], length(cmd));
|
|
// writeln('sendcommand3');
|
|
except writeln('EXCEPTION sending command to mplayer');
|
|
end;
|
|
end;
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
function TMPlayerClass.GetProcessOutput: string;
|
|
var AStringList: TStringList;
|
|
begin
|
|
// writeln('getoutput');
|
|
AStringList:=TStringList.Create;
|
|
try
|
|
if GetMPlayerPlaying then AStringList.LoadFromStream(MPlayerProcess.Output);
|
|
if AStringList.Count>0 then
|
|
Result:=AStringList.Strings[0]
|
|
else
|
|
Result := '';
|
|
// writeln(Result);
|
|
except
|
|
writeln('EXCEPTION reading mplayer output');result:='';
|
|
end;
|
|
//writeln('endget');
|
|
AStringList.Free;
|
|
end;
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
function TMPlayerClass.GetMPlayerPlaying: boolean;
|
|
begin
|
|
if assigned(MPlayerProcess)=false or (MPlayerProcess.Running=false) then result:=false else result:=true;
|
|
end;
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
constructor TMPlayerClass.create;
|
|
var tmps, tmppath: string;
|
|
i: integer;
|
|
begin
|
|
inherited;
|
|
|
|
// Find mplayer executable
|
|
FMplayerPath:='';
|
|
tmps:=GetEnvironmentVariable('PATH');
|
|
repeat
|
|
begin
|
|
i:=pos(':', tmps);
|
|
if i=0 then i:=Length(tmps);
|
|
tmppath:=IncludeTrailingPathDelimiter(copy(tmps,0,i-1))+MPLAYER_BINARY;
|
|
if FileExists(tmppath) then FMplayerPath:=tmppath
|
|
else Delete(tmps, 1, i);
|
|
end;
|
|
until (length(tmps)<=1) or (FMplayerPath<>'');
|
|
if FMplayerPath='' then begin
|
|
writeln('FATAL: Mplayer executable not found. Make sure it is properly installed in binary path');
|
|
end else DebugOutLn('Mplayer executable found in '+FMplayerPath, 2);
|
|
end;
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
destructor TMPlayerClass.destroy;
|
|
begin
|
|
|
|
end;
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
function TMPlayerClass.play(index: integer): byte;
|
|
var MPOptions: String;
|
|
begin
|
|
if (index<Playlist.ItemCount) and (index>=0) then begin
|
|
if (FileExists(playlist.items[index].path)) then begin
|
|
if FPlaying then stop;
|
|
MPlayerProcess:=TProcess.Create(nil);
|
|
|
|
if not UseExternalConfig then begin
|
|
MPOptions:='-slave -quiet -softvol';
|
|
if OutputMode=ALSAOUT then MPOptions:=MPOptions+' -ao alsa';
|
|
if OutputMode=OSSOUT then MPOptions:=MPOptions+' -ao oss';
|
|
end else MPOptions:='-include '+ExternalConfigFile;
|
|
|
|
MPOptions:=' -af volume=' + IntToStr(IntTodB(FVolume, 100)) +' '+ MPOptions;// -volume xx only supported with patched mplayer;
|
|
|
|
FPlaybackMode:=FILE_MODE;
|
|
//DebugOutLn('playing -> '+playlist.items[index].path, 1);
|
|
// writeln(StringReplace(playlist.items[index].path, '''', '''''', [rfReplaceAll]));
|
|
MPlayerProcess.CommandLine:=FMplayerPath+' '+MPOptions+' "'+playlist.items[index].path+'"';
|
|
|
|
DebugOutLn(MPlayerProcess.CommandLine,5);
|
|
FLastGet_Pos:=0;
|
|
MPlayerProcess.Options:= MPlayerProcess.Options + [poUsePipes, poDefaultErrorMode, poStderrToOutPut, poNoConsole];
|
|
MPlayerProcess.Execute;
|
|
|
|
if MPlayerProcess.Running then begin
|
|
FCurrentTrack:=index;
|
|
FPlaying:=true;
|
|
Playlist.Items[index].Played:=true;
|
|
result:=0;
|
|
end;
|
|
end else result:=1;
|
|
end else DebugOutLn('File not found ->'+playlist.items[index].path,0);
|
|
end;
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
function TMPlayerClass.play(url: string): byte;
|
|
var MPOptions: String;
|
|
Vol: real;
|
|
begin
|
|
if FPlaying then stop;
|
|
MPlayerProcess:=TProcess.Create(nil);
|
|
if not UseExternalConfig then begin
|
|
MPOptions:='-slave -quiet -softvol';
|
|
if OutputMode=ALSAOUT then MPOptions:=MPOptions+' -ao alsa';
|
|
if OutputMode=OSSOUT then MPOptions:=MPOptions+' -ao oss';
|
|
end else MPOptions:='-include '+ExternalConfigFile;
|
|
|
|
MPOptions:='-af volume=' + IntToStr(IntTodB(FVolume, 100)) +' '+ MPOptions;// -volume xx only supported with patched mplayer;
|
|
|
|
FPlaybackMode:=STREAMING_MODE;
|
|
DebugOutLn('playing -> '+url, 1);
|
|
MPlayerProcess.CommandLine:=FMplayerPath+' '+MPOptions+' "'+url+'"';
|
|
|
|
DebugOutLn(MPlayerProcess.CommandLine,5);
|
|
|
|
MPlayerProcess.Options:= MPlayerProcess.Options + [poUsePipes];
|
|
MPlayerProcess.Execute;
|
|
|
|
if MPlayerProcess.Running then begin
|
|
FPlaying:=true;
|
|
result:=0;
|
|
end;
|
|
end;
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
procedure TMPlayerClass.pause;
|
|
begin
|
|
if FPlaying and Assigned(MPlayerProcess) then begin
|
|
SendCommand('pause');
|
|
sleep(10);
|
|
writeln('pauseee');
|
|
FPaused:=not FPaused;
|
|
end;
|
|
end;
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
procedure TMPlayerClass.stop;
|
|
begin
|
|
if FPlaying and Assigned(MPlayerProcess) then begin
|
|
SendCommand('quit');
|
|
sleep(15);
|
|
if MPlayerProcess.Running then begin
|
|
sleep(50);
|
|
if MPlayerProcess.Running then
|
|
if MPlayerProcess.Terminate(0) then DebugOutLn('Mplayer stopped', 5)
|
|
else DebugOutLn('FATAL Mplayer process zombified',0);
|
|
end;
|
|
MPlayerProcess.Free;
|
|
end;
|
|
FCurrentTrack:=-1;
|
|
FPlaying:=false;
|
|
end;
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
function TMPlayerClass.next_track: byte;
|
|
var r:byte;
|
|
begin
|
|
r:=127;
|
|
if fplaying then begin
|
|
writeln('mnexttrack');
|
|
if FCurrentTrack<Playlist.ItemCount-1 then begin
|
|
r:=play(FCurrentTrack+1);
|
|
end;
|
|
end;
|
|
result:=r;
|
|
end;
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
function TMPlayerClass.prev_track: byte;
|
|
var r:byte;
|
|
begin
|
|
r:=127;
|
|
if fplaying then begin
|
|
if (FCurrentTrack<Playlist.ItemCount) and (FCurrentTrack>0) then begin
|
|
r:=play(FCurrentTrack-1);
|
|
end else
|
|
if (FCurrentTrack<Playlist.ItemCount) and (FCurrentTrack=0) then begin
|
|
r:=play(FCurrentTrack);
|
|
end;
|
|
end;
|
|
result:=r;
|
|
end;
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
function TMPlayerClass.Get_Stream_Status: TStreamStatus;
|
|
begin
|
|
Result:=STREAM_READY; //Impossible to get stream status from mplayer :(
|
|
end;
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
function TMPlayerClass.Get_TrackLength:longint;
|
|
var tmps: string;
|
|
i:integer;
|
|
time: real;
|
|
begin
|
|
if FPlaying and Assigned(MPlayerProcess) and MPlayerProcess.Running then begin
|
|
repeat begin
|
|
SendCommand('get_time_length');
|
|
sleep(5);
|
|
tmps:=GetProcessOutput;
|
|
end;
|
|
until pos('LENGTH', tmps)>0;
|
|
i:=LastDelimiter('=', tmps);
|
|
if i > 0 then begin
|
|
time:= StrToFloat(Copy(tmps, i+1, Length(tmps)));
|
|
time:=time*1000;
|
|
result:=round(time);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
function TMPlayerClass.Get_Time: longint;
|
|
var tmps: string;
|
|
i:integer;
|
|
time: real;
|
|
begin
|
|
if GetMPlayerPlaying then begin
|
|
i:=0;
|
|
repeat begin
|
|
SendCommand('get_property time_pos');
|
|
sleep(8);
|
|
tmps:=GetProcessOutput;
|
|
inc(i);
|
|
end;
|
|
until (pos('time_pos', tmps)>0) or (i>=3);
|
|
i:=LastDelimiter('=', tmps);
|
|
if i > 0 then begin
|
|
time:= StrToFloat(Copy(tmps, i+1, Length(tmps)));
|
|
time:=time*1000;
|
|
result:=round(time);
|
|
end else result:=-1;
|
|
end else result:=-1;
|
|
end;
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
function TMPlayerClass.Get_TimeStr: string;
|
|
begin
|
|
result:=MSecondsToFmtStr(Get_Time);
|
|
end;
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
function TMPlayerClass.Get_TimeRemainingStr: string;
|
|
begin
|
|
result:= '-' + MSecondsToFmtStr(Get_TrackLength - Get_Time);
|
|
end;
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
function TMPlayerClass.Get_FilePosition: longint;
|
|
var tmps: string;
|
|
i:integer;
|
|
begin
|
|
if GetMPlayerPlaying then begin
|
|
i:=0;
|
|
repeat begin
|
|
SendCommand('get_property percent_pos');
|
|
sleep(8);
|
|
tmps:=GetProcessOutput;
|
|
inc(i);
|
|
// writeln('jj');
|
|
end;
|
|
until (pos('percent_pos', tmps)>0) or (i>=5);
|
|
// writeln('getpos');
|
|
i:=LastDelimiter('=', tmps);
|
|
if i > 0 then begin
|
|
FLastGet_Pos:=round(StrToFloat(Copy(tmps, i+1, Length(tmps)-i)));
|
|
result:=FLastGet_Pos;
|
|
end else result:=-1;
|
|
end else result:=-1;
|
|
if (result=-1) and (FLastGet_Pos>0) then Result:=100;
|
|
end;
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
function TMPlayerClass.get_FileLength: longint;
|
|
var tmps: string;
|
|
i:integer;
|
|
begin
|
|
if FPlaying and Assigned(MPlayerProcess) and MPlayerProcess.Running then begin
|
|
SendCommand('get_property stream_length');
|
|
sleep(10);
|
|
tmps:=GetProcessOutput;
|
|
i:=LastDelimiter('=', tmps);
|
|
if i > 0 then begin
|
|
result:= Trunc(StrToFloat(Copy(tmps, i+1, Length(tmps)-i)));
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
procedure TMPlayerClass.Set_Time(ms: longint);
|
|
begin
|
|
|
|
end;
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
procedure TMPlayerClass.Set_FilePosition(fpos: longint);
|
|
begin
|
|
if FPlaying and Assigned(MPlayerProcess) and MPlayerProcess.Running then begin
|
|
SendCommand('set_property percent_pos '+IntToStr(fpos));
|
|
sleep(20);
|
|
end;
|
|
end;
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
procedure TMPlayerClass.Set_Volume(vol: byte);
|
|
var commandstr: string;
|
|
begin
|
|
FVolume:=vol;
|
|
if FPlaying and Assigned(MPlayerProcess) and MPlayerProcess.Running then begin
|
|
if vol<0 then vol:=0;
|
|
if vol>100 then vol:=100;
|
|
SendCommand('set_property volume '+IntToStr(vol)+'/1');
|
|
end;
|
|
end;
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
procedure TMPlayerClass.Mute;
|
|
var commandstr: string;
|
|
begin
|
|
if FPlaying and Assigned(MPlayerProcess) and MPlayerProcess.Running then begin
|
|
SendCommand('mute');
|
|
end;
|
|
end;
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
function TMPlayerClass.Muted: boolean;
|
|
var tmps, s: string;
|
|
i:integer;
|
|
begin
|
|
if FPlaying and Assigned(MPlayerProcess) and MPlayerProcess.Running then begin
|
|
repeat begin
|
|
SendCommand('get_property mute');
|
|
sleep(5);
|
|
tmps:=GetProcessOutput;
|
|
end;
|
|
until pos('mute', tmps)>0;
|
|
i:=LastDelimiter('=', tmps);
|
|
if i > 0 then begin
|
|
s:=Copy(tmps, i+1, Length(tmps)-i);
|
|
if s='yes' then Result:=true else Result:=false;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
function TMPlayerClass.setMplayerBinaryDir(dir: string): boolean;
|
|
begin
|
|
dir:=IncludeTrailingPathDelimiter(dir);
|
|
if FileExists(dir+MPLAYER_BINARY) then begin
|
|
result:=true;
|
|
FMPlayerPath:=dir+MPLAYER_BINARY;
|
|
WriteLn('Manually set MPlayer path to '+FMPlayerPath);
|
|
end else begin
|
|
result:=false;
|
|
FMPlayerPath:='';
|
|
end;
|
|
end;
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
end.
|
|
|