| 1 | // MPlayer slave command list: http://www.mplayerhq.hu/DOCS/tech/slave.txt
|
|---|
| 2 |
|
|---|
| 3 | unit AudioSystemMPlayer;
|
|---|
| 4 |
|
|---|
| 5 | {$I CoolAudioConfig.inc}
|
|---|
| 6 |
|
|---|
| 7 | interface
|
|---|
| 8 |
|
|---|
| 9 | uses
|
|---|
| 10 | Classes, SysUtils, AudioSystem, Process, Math, Dialogs, DateUtils;
|
|---|
| 11 |
|
|---|
| 12 | const
|
|---|
| 13 | {$ifdef Unix}
|
|---|
| 14 | MPlayerExecutableName = 'mplayer';
|
|---|
| 15 | {$endif}
|
|---|
| 16 | {$ifdef Windows}
|
|---|
| 17 | MPlayerExecutableName = 'mplayer.exe';
|
|---|
| 18 | {$endif}
|
|---|
| 19 |
|
|---|
| 20 | type
|
|---|
| 21 | { TAudioSystemMPlayer }
|
|---|
| 22 |
|
|---|
| 23 | TAudioSystemMPlayer = class(TAudioSystem)
|
|---|
| 24 | protected
|
|---|
| 25 | FPath: string;
|
|---|
| 26 | procedure SetOutputMode(AValue: TOutputDriver); override;
|
|---|
| 27 | public
|
|---|
| 28 | function FindPath: string;
|
|---|
| 29 | constructor Create(AOwner: TComponent); override;
|
|---|
| 30 | destructor Destroy; override;
|
|---|
| 31 | function GetMediaPlayerDriverClass: TMediaPlayerDriverClass; override;
|
|---|
| 32 | property Path: string read FPath write FPath;
|
|---|
| 33 | end;
|
|---|
| 34 |
|
|---|
| 35 | { TPlayerMPlayer }
|
|---|
| 36 |
|
|---|
| 37 | TPlayerMPlayer = class(TMediaPlayerDriver)
|
|---|
| 38 | protected
|
|---|
| 39 | FProcess: TProcess;
|
|---|
| 40 | FVolume: Real;
|
|---|
| 41 | function GetProcessOutput: string;
|
|---|
| 42 | procedure SendCommand(Command: string);
|
|---|
| 43 | function GetLength: TDateTime; override;
|
|---|
| 44 | function GetPosition: TDateTime; override;
|
|---|
| 45 | function GetVolume: Real; override;
|
|---|
| 46 | function GetMuted: Boolean; override;
|
|---|
| 47 | procedure SetPosition(AValue: TDateTime); override;
|
|---|
| 48 | procedure SetVolume(AValue: Real); override;
|
|---|
| 49 | procedure SetMuted(AValue: Boolean); override;
|
|---|
| 50 | procedure SetFileName(AValue: string); override;
|
|---|
| 51 | public
|
|---|
| 52 | procedure Play; override;
|
|---|
| 53 | procedure Pause; override;
|
|---|
| 54 | procedure Stop; override;
|
|---|
| 55 | constructor Create; override;
|
|---|
| 56 | destructor Destroy; override;
|
|---|
| 57 | end;
|
|---|
| 58 |
|
|---|
| 59 | resourcestring
|
|---|
| 60 | SMPlayerNotFound = 'MPlayer executable not found. Make sure it is properly installed in binary path';
|
|---|
| 61 | SSendCommandException = 'Exception occured during sending command to MPlayer';
|
|---|
| 62 | SErrorReadingOutput = 'Exception while reading MPlayer output';
|
|---|
| 63 | SCantStopProcess = 'Can''t stop Mplayer process';
|
|---|
| 64 |
|
|---|
| 65 |
|
|---|
| 66 | implementation
|
|---|
| 67 |
|
|---|
| 68 | function StrToFloatPoint(Value: string): Extended;
|
|---|
| 69 | var
|
|---|
| 70 | FPointSeparator: TFormatSettings;
|
|---|
| 71 | begin
|
|---|
| 72 | // Format seetings to convert a string to a float
|
|---|
| 73 | FPointSeparator := DefaultFormatSettings;
|
|---|
| 74 | FPointSeparator.DecimalSeparator := '.';
|
|---|
| 75 | FPointSeparator.ThousandSeparator := '#';// disable the thousand separator
|
|---|
| 76 | Result := StrToFloat(Value, FPointSeparator);
|
|---|
| 77 | end;
|
|---|
| 78 |
|
|---|
| 79 | function FloatPointToStr(Value: Extended): string;
|
|---|
| 80 | var
|
|---|
| 81 | FPointSeparator: TFormatSettings;
|
|---|
| 82 | begin
|
|---|
| 83 | // Format seetings to convert a string to a float
|
|---|
| 84 | FPointSeparator := DefaultFormatSettings;
|
|---|
| 85 | FPointSeparator.DecimalSeparator := '.';
|
|---|
| 86 | FPointSeparator.ThousandSeparator := '#';// disable the thousand separator
|
|---|
| 87 | Result := FloatToStr(Value, FPointSeparator);
|
|---|
| 88 | end;
|
|---|
| 89 |
|
|---|
| 90 | { TAudioSystemMPlayer }
|
|---|
| 91 |
|
|---|
| 92 | procedure TAudioSystemMPlayer.SetOutputMode(AValue: TOutputDriver);
|
|---|
| 93 | begin
|
|---|
| 94 | inherited SetOutputMode(AValue);
|
|---|
| 95 | end;
|
|---|
| 96 |
|
|---|
| 97 | function TAudioSystemMPlayer.FindPath: string;
|
|---|
| 98 | var
|
|---|
| 99 | tmps: string;
|
|---|
| 100 | tmppath: string;
|
|---|
| 101 | I: Integer;
|
|---|
| 102 | begin
|
|---|
| 103 | Result := '';
|
|---|
| 104 | {$ifdef Darwin}
|
|---|
| 105 | {$else}
|
|---|
| 106 | // Searches for MPlayer in the PATH
|
|---|
| 107 | tmps := GetEnvironmentVariable('PATH');
|
|---|
| 108 | repeat
|
|---|
| 109 | I := Pos(PathSeparator, tmps);
|
|---|
| 110 | if I = 0 then I := Length(tmps);
|
|---|
| 111 | tmppath := IncludeTrailingPathDelimiter(Copy(tmps, 0, I - 1)) + MPlayerExecutableName;
|
|---|
| 112 | if FileExists(tmppath) then Result := tmppath
|
|---|
| 113 | else Delete(tmps, 1, I);
|
|---|
| 114 | until (Length(tmps) <= 1) or (Result <> '');
|
|---|
| 115 | {$endif}
|
|---|
| 116 | if Result = '' then raise Exception.Create(SMPlayerNotFound);
|
|---|
| 117 | end;
|
|---|
| 118 |
|
|---|
| 119 | constructor TAudioSystemMPlayer.Create(AOwner: TComponent);
|
|---|
| 120 | begin
|
|---|
| 121 | inherited;
|
|---|
| 122 | FPath := FindPath;
|
|---|
| 123 | end;
|
|---|
| 124 |
|
|---|
| 125 | destructor TAudioSystemMPlayer.Destroy;
|
|---|
| 126 | begin
|
|---|
| 127 | inherited;
|
|---|
| 128 | end;
|
|---|
| 129 |
|
|---|
| 130 | function TAudioSystemMPlayer.GetMediaPlayerDriverClass: TMediaPlayerDriverClass;
|
|---|
| 131 | begin
|
|---|
| 132 | Result := TPlayerMPlayer;
|
|---|
| 133 | end;
|
|---|
| 134 |
|
|---|
| 135 | { TPlayerMPlayer }
|
|---|
| 136 |
|
|---|
| 137 | procedure TPlayerMPlayer.SendCommand(Command: string);
|
|---|
| 138 | begin
|
|---|
| 139 | Command := Command + #10; // MPLayer always needs #10 as Lineending, no matter if win32 or linux
|
|---|
| 140 | try
|
|---|
| 141 | if FProcess.Running then FProcess.Input.Write(Command[1], System.Length(Command));
|
|---|
| 142 | except
|
|---|
| 143 | raise Exception.Create(SSendCommandException);
|
|---|
| 144 | end;
|
|---|
| 145 | end;
|
|---|
| 146 |
|
|---|
| 147 | function TPlayerMPlayer.GetLength: TDateTime;
|
|---|
| 148 | var
|
|---|
| 149 | tmps: string;
|
|---|
| 150 | I: Integer;
|
|---|
| 151 | Time: Real;
|
|---|
| 152 | begin
|
|---|
| 153 | if FPlaying and fProcess.Running then begin
|
|---|
| 154 | repeat
|
|---|
| 155 | SendCommand('get_time_length');
|
|---|
| 156 | Sleep(5);
|
|---|
| 157 | tmps := GetProcessOutput;
|
|---|
| 158 | until Pos('LENGTH', tmps) > 0;
|
|---|
| 159 | I := LastDelimiter('=', tmps);
|
|---|
| 160 | if I > 0 then begin
|
|---|
| 161 | Time := StrToFloatPoint(Copy(tmps, I + 1, System.Length(tmps)));
|
|---|
| 162 | Result := Time * OneSecond;
|
|---|
| 163 | end;
|
|---|
| 164 | end;
|
|---|
| 165 | end;
|
|---|
| 166 |
|
|---|
| 167 | function TPlayerMPlayer.GetPosition: TDateTime;
|
|---|
| 168 | var
|
|---|
| 169 | tmps: string;
|
|---|
| 170 | I: Integer;
|
|---|
| 171 | Time: Real;
|
|---|
| 172 | begin
|
|---|
| 173 | if FProcess.Running then begin
|
|---|
| 174 | I := 0;
|
|---|
| 175 | repeat
|
|---|
| 176 | SendCommand('get_property time_pos');
|
|---|
| 177 | Sleep(8);
|
|---|
| 178 | tmps := GetProcessOutput;
|
|---|
| 179 | Inc(I);
|
|---|
| 180 | until (Pos('time_pos', tmps) > 0) or (I >= 3);
|
|---|
| 181 | I := LastDelimiter('=', tmps);
|
|---|
| 182 | if I > 0 then begin
|
|---|
| 183 | Time := StrToFloatPoint(Copy(tmps, I + 1, System.Length(tmps)));
|
|---|
| 184 | Result := Time * OneSecond;
|
|---|
| 185 | end else Result := -1;
|
|---|
| 186 | end else Result := -1;
|
|---|
| 187 | end;
|
|---|
| 188 |
|
|---|
| 189 | function TPlayerMPlayer.GetVolume: Real;
|
|---|
| 190 | begin
|
|---|
| 191 | Result := 0;
|
|---|
| 192 | end;
|
|---|
| 193 |
|
|---|
| 194 | function TPlayerMPlayer.GetMuted: Boolean;
|
|---|
| 195 | var
|
|---|
| 196 | tmps, s: string;
|
|---|
| 197 | I: Integer;
|
|---|
| 198 | begin
|
|---|
| 199 | if FPlaying and FProcess.Running then begin
|
|---|
| 200 | repeat
|
|---|
| 201 | SendCommand('get_property mute');
|
|---|
| 202 | Sleep(5);
|
|---|
| 203 | tmps := GetProcessOutput;
|
|---|
| 204 | until Pos('mute', tmps) > 0;
|
|---|
| 205 | i := LastDelimiter('=', tmps);
|
|---|
| 206 | if i > 0 then begin
|
|---|
| 207 | s := Copy(tmps, i + 1, System.Length(tmps) - i);
|
|---|
| 208 | Result := s = 'yes';
|
|---|
| 209 | end;
|
|---|
| 210 | end;
|
|---|
| 211 | end;
|
|---|
| 212 |
|
|---|
| 213 | procedure TPlayerMPlayer.SetPosition(AValue: TDateTime);
|
|---|
| 214 | begin
|
|---|
| 215 | if FPlaying and FProcess.Running then begin
|
|---|
| 216 | SendCommand('set_property time_pos ' + FloatPointToStr(AValue / OneSecond));
|
|---|
| 217 | end;
|
|---|
| 218 | end;
|
|---|
| 219 |
|
|---|
| 220 | procedure TPlayerMPlayer.SetVolume(AValue: Real);
|
|---|
| 221 | begin
|
|---|
| 222 | if FVolume = AValue then Exit;
|
|---|
| 223 | FVolume := AValue;
|
|---|
| 224 | if FPlaying and FProcess.Running then begin
|
|---|
| 225 | if AValue < 0 then AValue := 0;
|
|---|
| 226 | if AValue > 1 then AValue := 1;
|
|---|
| 227 | SendCommand('set_property volume ' + IntToStr(Round(AValue * 100)) + '/1');
|
|---|
| 228 | end;
|
|---|
| 229 | end;
|
|---|
| 230 |
|
|---|
| 231 | procedure TPlayerMPlayer.SetMuted(AValue: Boolean);
|
|---|
| 232 | begin
|
|---|
| 233 | if FPlaying and FProcess.Running then
|
|---|
| 234 | SendCommand('mute');
|
|---|
| 235 | end;
|
|---|
| 236 |
|
|---|
| 237 | procedure TPlayerMPlayer.SetFileName(AValue: string);
|
|---|
| 238 | begin
|
|---|
| 239 | inherited SetFileName(AValue);
|
|---|
| 240 | end;
|
|---|
| 241 |
|
|---|
| 242 | function IntTodB(I, Ref: Longint): Integer;
|
|---|
| 243 | var
|
|---|
| 244 | dB: Real;
|
|---|
| 245 | begin
|
|---|
| 246 | if I = 0 then db := 0.001 else dB := I;
|
|---|
| 247 | dB := 20 * log10(dB / ref);
|
|---|
| 248 | Result := Round(dB);
|
|---|
| 249 | end;
|
|---|
| 250 |
|
|---|
| 251 | procedure TPlayerMPlayer.Play;
|
|---|
| 252 | var
|
|---|
| 253 | MPOptions: String;
|
|---|
| 254 | begin
|
|---|
| 255 | if FPlaying then Stop;
|
|---|
| 256 | //FProcess := TProcess.Create(nil);
|
|---|
| 257 | MPOptions := '-slave -quiet -softvol';
|
|---|
| 258 | if AudioSystem.OutputMode = omAlsa then MPOptions := MPOptions + ' -ao alsa';
|
|---|
| 259 | if AudioSystem.OutputMode = omOSS then MPOptions := MPOptions + ' -ao oss';
|
|---|
| 260 | if AudioSystem.OutputMode = omWin32 then MPOptions := MPOptions + ' -ao win32';
|
|---|
| 261 | if AudioSystem.OutputMode = omDirectX then MPOptions := MPOptions + ' -ao dsound';
|
|---|
| 262 |
|
|---|
| 263 | //MPOptions := '-af volume=' + IntToStr(IntTodB(Round(FVolume * 100), 100)) + ' ' + MPOptions;// -volume xx only supported with patched mplayer;
|
|---|
| 264 |
|
|---|
| 265 | FProcess.CommandLine := TAudioSystemMPlayer(AudioSystem).FPath + ' ' + MPOptions + ' "' + UTF8Decode(FFileName) + '"';
|
|---|
| 266 | FProcess.Options := FProcess.Options + [poUsePipes, poDefaultErrorMode, poStderrToOutPut, poNoConsole];
|
|---|
| 267 | //InputBox('', '', FProcess.CommandLine);
|
|---|
| 268 | FProcess.Execute;
|
|---|
| 269 |
|
|---|
| 270 | if FProcess.Running then begin
|
|---|
| 271 | FPlaying := True;
|
|---|
| 272 | end;
|
|---|
| 273 | end;
|
|---|
| 274 |
|
|---|
| 275 | procedure TPlayerMPlayer.Pause;
|
|---|
| 276 | begin
|
|---|
| 277 | if FPlaying then begin
|
|---|
| 278 | SendCommand('pause');
|
|---|
| 279 | Sleep(10);
|
|---|
| 280 | //FPaused := not FPaused;
|
|---|
| 281 | end;
|
|---|
| 282 | end;
|
|---|
| 283 |
|
|---|
| 284 | procedure TPlayerMPlayer.Stop;
|
|---|
| 285 | begin
|
|---|
| 286 | if FPlaying then begin
|
|---|
| 287 | SendCommand('quit');
|
|---|
| 288 | Sleep(15);
|
|---|
| 289 | if FProcess.Running then begin
|
|---|
| 290 | Sleep(50);
|
|---|
| 291 | if FProcess.Running then
|
|---|
| 292 | if not FProcess.Terminate(0) then
|
|---|
| 293 | raise Exception.Create(SCantStopProcess);
|
|---|
| 294 | end;
|
|---|
| 295 | end;
|
|---|
| 296 | FPlaying := False;
|
|---|
| 297 | end;
|
|---|
| 298 |
|
|---|
| 299 | constructor TPlayerMPlayer.Create;
|
|---|
| 300 | begin
|
|---|
| 301 | inherited;
|
|---|
| 302 | FProcess := TProcess.Create(nil);
|
|---|
| 303 | end;
|
|---|
| 304 |
|
|---|
| 305 | destructor TPlayerMPlayer.Destroy;
|
|---|
| 306 | begin
|
|---|
| 307 | Stop;
|
|---|
| 308 | FreeAndNil(FProcess);
|
|---|
| 309 | inherited;
|
|---|
| 310 | end;
|
|---|
| 311 |
|
|---|
| 312 | function TPlayerMPlayer.GetProcessOutput: string;
|
|---|
| 313 | var
|
|---|
| 314 | AStringList: TStringList;
|
|---|
| 315 | begin
|
|---|
| 316 | try
|
|---|
| 317 | AStringList:=TStringList.Create;
|
|---|
| 318 | try
|
|---|
| 319 | if FProcess.Running then AStringList.LoadFromStream(FProcess.Output);
|
|---|
| 320 | if AStringList.Count > 0 then
|
|---|
| 321 | Result := AStringList.Strings[0]
|
|---|
| 322 | else Result := '';
|
|---|
| 323 | except
|
|---|
| 324 | Result := '';
|
|---|
| 325 | raise Exception.Create(SErrorReadingOutput);
|
|---|
| 326 | end;
|
|---|
| 327 | finally
|
|---|
| 328 | AStringList.Free;
|
|---|
| 329 | end;
|
|---|
| 330 | end;
|
|---|
| 331 |
|
|---|
| 332 | end.
|
|---|
| 333 |
|
|---|