2011-07-21 09:39:48 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
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);
|
2011-07-26 06:36:09 +00:00
|
|
|
if AStringList.Count>0 then
|
|
|
|
Result:=AStringList.Strings[0]
|
|
|
|
else
|
|
|
|
Result := '';
|
2011-07-21 09:39:48 +00:00
|
|
|
// 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.
|
|
|
|
|