DSPack2.33播放Stream的方法。 ( 积分: 0 )

  • 主题发起人 主题发起人 alv
  • 开始时间 开始时间
A

alv

Unregistered / Unconfirmed
GUEST, unregistred user!
在网上翻了好几天,终于找到了怎么用DSPack播放Stream(TStream类),
下面将我使用的方法贴出来,跟大家一起交流一下:

我使用的方法是直接修改TFilterGraph类,我用的是DSPack2.33,
由于DSPack.pas太大,我就把我修改的地方贴出来:
添加: uses Filter, Config;

添加:
{@exclude}
TControlEvent = (
cePlay,
cePause,
ceStop,
ceFileRendering,
ceFileRendered,
ceStreamRendering, //添加的
ceStreamRendered, //地方
ceDVDRendering,
ceDVDRendered,
ceActive
);

type
TFilterGraph = class (TComponent,IAMGraphBuilderCallback,
IAMFilterGraphCallback,IServiceProvider)
private
fAsyncEx : IBaseFilter;
fAsyncExControl : IAsyncExControl;
fPin : IPin;
public
function RendStream(Stream: TStream): HRESULT;

.
.
.
function TFilterGraph.RendStream(Stream: TStream): HRESULT;
begin

Result := S_FALSE;
if Assigned(FFilterGraph) then

begin

ControlEvents(ceStreamRendering);
fAsyncEX := TAsyncEx.Create;
CheckDSError(fAsyncEx.QueryInterface(IID_IAsyncExControl, fAsyncExControl));
CheckDSError(fAsyncExControl.SetLoadFromStream(TStreamAdapter.Create(Stream, soOwned),Stream.Size));
CheckDSError(fAsyncEx.FindPin('StreamOut', fPin));
CheckDSError(fFilterGraph.AddFilter(fAsyncEX,StringToOleStr('AsyncEx')));
Result := fFilterGraph.Render(fPin);
ControlEvents(ceStreamRendered);
end;

end;
 
下面我把其中用到的TAsyncEx类贴上来:
Filter.pas

unit Filter;

(*********************************************************************
* The contents of this file are used with permission, subject to *
* the Mozilla Public License Version 1.1 (the "License");
you may *
* not use this file except in compliance with the License. You may *
* obtain a copy of the License at *
* http://www.mozilla.org/MPL/MPL-1.1.html *
* *
* Software distributed under the License is distributed on an *
* "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or *
* implied. See the License for the specific language governing *
* rights and limitations under the License. *
* *
* (C) 2004 Martin Offenwanger: coder@dsplayer.de *
*********************************************************************)
{
@author(Martin Offenwanger: coder@dsplayer.de)
@created(Apr 22, 2004)
@lastmod(Sep 09, 2004)
}

interface

uses
WorkerThread, ICYParser, ActiveX, Classes, DirectShow9, BaseClass, Windows,
Config, StringQueue, Forms;

type
TAsyncEx = class(TBCBaseFilter, IFileSourceFilter, IAsyncExControl)
private
// the actual playback location "supported: URL, File, stream"
FFileName: string;
// all loaded filtes will be wrapped into a Filestream
FFilestream: TFileStream;
// Basepin Object
FPin: TBCBasePin;
FLock: TBCCritSec;
FStreamsize: int64;
// Pin state flag
FPinActive: boolean;
// Prebuffer loop flag
Fexitloop: boolean;
// Ripper Flag
FRipstream: boolean;
FPath: string;
FFile: string;
FState: _FilterState;
function GetOutPin: IPin;
// creates a Filter pin if streamEnabled=true (URL or IStream)
procedure CreateFilterPin(AStream: IStream;
StreamEnabled: boolean = false;
URLPin: boolean = false);
overload;
// helper function
function ParseUrl(Url: string;
out host: string;
out port: string;
out Location: string): boolean;
public
constructor Create;
constructor CreateFromFactory(Factory: TBCClassFactory;
const Controller: IUnknown);
override;
destructor Destroy;
override;
function GetPin(n: Integer): TBCBasePin;
override;
function GetPinCount: Integer;
override;
function Run(tStart: TReferenceTime): HRESULT;
override;
stdcall;
function Stop: HRESULT;
virtual;
stdcall;
function Pause: HRESULT;
virtual;
stdcall;
function NonDelegatingRelease: Integer;
override;
stdcall;
// IFileSourceFilter
function Load(pszFileName: PWCHAR;
const pmt: PAMMediaType): HRESULT;
stdcall;
function GetCurFile(out ppszFileName: PWideChar;
pmt: PAMMediaType): HRESULT;
stdcall;
// IDSPlayerAsyncSourceControl
function SetLoadFromStream(Stream: IStream;
Length: int64): HRESULT;
stdcall;
function SetConnectToIp(Host: PChar;
Port: PChar;
Location: PChar;
PreBuffersize: integer;
MetaData: LongBool): HRESULT;
stdcall;
function SetConnectToURL(URL: PChar;
PreBuffersize: integer;
MetaData: LongBool): HRESULT;
stdcall;
function SetBuffersize(BufferSize: integer): HRESULT;
stdcall;
function GetBuffersize(out BufferSize: integer): HRESULT;
stdcall;
function SetRipStream(Ripstream: LongBool;
Path: PChar;
Filename: PChar): HRESULT;
stdcall;
function GetRipStream(out Ripstream: LongBool;
out FileO: PChar): HRESULT;
stdcall;
function SetCallBack(CallBack: IAsyncExCallBack): HRESULT;
stdcall;
function FreeCallback(): HRESULT;
stdcall;
function ExitAllLoops(): HRESULT;
stdcall;
// properties
property OutPin: IPin read GetOutPin;
// returns current state
function GetState(MSecs: DWord;
out State: TFilterState): HResult;
override;
stdcall;
end;


implementation

uses
SysUtils, StreamOutPin;

function TAsyncEx.GetState(MSecs: DWord;
out State: TFilterState): HResult;
stdcall;
begin

State := FState;
Result := S_OK;
end;


function TAsyncEx.ExitAllLoops(): HRESULT;
stdcall;
begin

// FLock.Lock;
GFExit := true;
end;


function TAsyncEx.Run(tStart: TReferenceTime): HRESULT;
stdcall;
begin

if (FGRaph <> nil) and (FPin <> nil) and (GFConnected) then

begin

TStreamOutPin(FPin).setActiveGraph(FGRaph);
RESULT := S_OK;
end
else

RESULT := E_FAIL;
if result = S_OK then

result := (inherited Run(tStart));
if result = S_OK then

FState := State_Running;
end;


function TAsyncEx.Stop: HRESULT;
stdcall;
begin

FState := State_Stopped;
result := (inherited Stop);
end;


function TAsyncEx.Pause: HRESULT;
stdcall;
begin

FState := State_Paused;
result := (inherited Stop);
end;


// IDSPlayerAsyncSourceControl begin


function TAsyncEx.SetConnectToURL(URL: PChar;
PreBuffersize: integer;
MetaData:
LongBool): HRESULT;
stdcall;
var
Host, Port, Location, URLO: string;
begin

FLock.lock;
URLO := copy(URL, 1, length(URL));
if not ParseUrl(URLO, Host, Port, Location) then

begin

result := E_FAIL;
exit;
end;

FLock.unlock;
result := SetConnectToIp(PChar(Host), PChar(Port), PChar(Location),
PreBuffersize, MetaData);
end;


function TAsyncEx.SetConnectToIp(Host: PChar;
Port: PChar;
Location: PChar;
PreBuffersize: integer;
MetaData: LongBool): HRESULT;
stdcall;
var
Datawritten: boolean;
Application: TApplication;
i: integer;
Buffer: string;
Avdata: int64;
begin

if GFExit then

begin

Result := E_FAIL;
exit;
end;

if GFConnected then

begin

Result := E_FAIL;
exit;
end;

try
FLock.Lock;
GFPreBufferSize := PreBuffersize;
GFStringQueue := TStringQueue.Create;
Datawritten := false;
Application := TApplication.Create(nil);
i := 0;
Avdata := 0;
Buffer := '';
GFFileName := 'N/A';
if GFExit then

begin

Result := E_FAIL;
exit;
end;

if GFConnected then

begin

Result := E_FAIL;
exit;
end;

CreateFilterPin(TStreamAdapter.Create(nil, soOwned), true, true);
if FPin <> nil then

TStreamOutPin(FPin).DoConnect(copy(Host, 0, system.length(Host)),
copy(Port, 0, system.length(Port)),
copy(Location, 0, system.length(Location)),
MetaData);
SetRipStream(FRipstream, PChar(FPath), PChar(FFile));

while not Datawrittendo

begin

if GFExit then

begin

Result := E_FAIL;
FLock.UnLock;
exit;
end;

if g_threadedShoutcastStream = nil then

begin

Result := E_FAIL;
FLock.UnLock;
exit;
end;

if GFStringQueue = nil then

begin

Result := E_FAIL;
FLock.UnLock;
exit;
end;

Sleep(1);
if GFConnected then

begin

Result := E_FAIL;
FLock.UnLock;
exit;
end;


if (GFFilterCallBack <> nil) and
(PreBuffersize > 0) and
(g_threadedShoutcastStream <> nil) then

GFFilterCallBack.AsyncExFilterState(false, true, false,
false, (trunc((Avdata * 100) / PreBuffersize)));

Application.ProcessMessages;

if GFExit then

begin

result := E_FAIL;
Application.Destroy;
FLock.UnLock;
exit;
end
else
if GFStringQueue = nil then

begin

Result := E_FAIL;
Application.Destroy;
FLock.UnLock;
exit;
end;

if GFStringQueue.getcount > i then

begin

Buffer := Buffer + GFStringQueue.getitem(i);
inc(i);
end;

if (PreBuffersize <= Avdata) then

Datawritten := true
else

Avdata := system.length(Buffer);
end;

Application.Destroy;
Result := S_OK;
except
result := E_FAIL;
end;

FLock.UnLock;
end;


function TAsyncEx.SetBuffersize(BufferSize: integer): HRESULT;
stdcall;
begin

Result := S_OK;
{ if the buffersize is too small and when the min buffersize is not available
the min buffersize will be automaticly set in TAsyncIO.SyncRead.
Reason: at this point the min buffersize might not known }
if GFMinBuffersize < BufferSize then

// copy the value is slower but more savety to prevent crashes
GFBufferSize := strtoint(copy(inttostr(BufferSize), 1,
length(inttostr(BufferSize))));
end;


function TAsyncEx.GetBuffersize(out BufferSize: integer): HRESULT;
stdcall;
begin

Result := S_OK;
// copy the value is slower but more safety, to prevent crashes
BufferSize := strtoint(copy(inttostr(GFBufferSize), 1,
length(inttostr(GFBufferSize))));
end;


function TAsyncEx.SetRipStream(Ripstream: LongBool;
Path: PChar;
Filename: PChar): HRESULT;
stdcall;
begin

FRipstream := Ripstream;
FPath := copy(Path, 1, length(Path));
FFile := copy(Filename, 1, length(Filename));
RESULT := S_OK;
if g_threadedShoutcastStream <> nil then

begin

g_threadedShoutcastStream.SetRipStream(Ripstream, Path, FFile);
RESULT := S_OK;
end;

end;


// TAsyncEx.GetRipStream is not implemented yet

function TAsyncEx.GetRipStream(out Ripstream: LongBool;
out FileO: PChar): HRESULT;
stdcall;
var
fileL: string;
{*l_ripstream: boolean;*}
begin

fileL := '';
{*l_ripstream := false;*}
RESULT := E_FAIL;
{* if g_shoutCastStream <> nil then

begin

g_shoutCastStream.get_ripStream(l_ripstream,l_file);
Ripstream := l_ripstream;
FileO := copy(l_file,1,length(l_file));
RESULT := S_OK;
end;

*}
end;


function TAsyncEx.SetLoadFromStream(Stream: IStream;
Length: int64): HRESULT;
stdcall;
begin

FStreamsize := Length;
CreateFilterPin(Stream, true);
// CreateFilterPin(TStreamAdapter.Create(@Stream, soOwned), true);
GFFileName := 'In TStream Mode is Filename not available';
Result := S_OK;
end;


function TAsyncEx.SetCallBack(CallBack: IAsyncExCallBack): HRESULT;
stdcall;
begin

GFFilterCallBack := CallBack;
Result := S_OK;
end;


function TAsyncEx.FreeCallback(): HRESULT;
stdcall;
begin

if Assigned(GFFilterCallBack) then

begin

GFFilterCallBack.AsyncExICYNotice(ICYName, 'N/A');
GFFilterCallBack.AsyncExICYNotice(ICYGenre, 'N/A');
GFFilterCallBack.AsyncExICYNotice(ICYURL, 'N/A');
GFFilterCallBack.AsyncExICYNotice(ICYBitrate, 'N/A');
GFFilterCallBack.AsyncExFilterState(false, false, false, false, 0);
GFFilterCallBack := nil;
end;

result := S_OK;
end;

// IDSPlayerAsyncSourceControl end

// IFileSourceFilter begin


function TAsyncEx.Load(pszFileName: PWCHAR;
const pmt: PAMMediaType): HRESULT;
stdcall;
begin

if Length(pszFileName) > MAX_PATH then

begin

result := ERROR_FILENAME_EXCED_RANGE;
exit;
end;

FFileName := GCFFilterID + ' (' + ExtractFileName(pszFileName) + ')';
FFilestream := TFileStream.Create(pszFileName, fmOpenRead or
fmShareDenyWrite);
FStreamsize := FFilestream.Size;
CreateFilterPin(TStreamAdapter.Create(FFilestream, soOwned), true);
GFFileName := pszFileName;
if FFileName = pszFileName then

Result := E_OUTOFMEMORY
else

result := S_OK;
end;


function TAsyncEx.GetCurFile(out ppszFileName: PWideChar;
pmt: PAMMediaType): HRESULT;
begin

// no need to set a Mediatype at this point
ppszFileName := StringToOleStr(copy(FFileName, 1, Length(FFileName)));
result := S_OK;
end;

// IFileSourceFilter end

constructor TAsyncEx.Create;
begin

FLock := TBCCritSec.Create;
FState := State_Stopped;
FFilestream := nil;
FFile := '';
g_threadedShoutcastStream := nil;
GFFilterCallBack := nil;
// 300kb as default
GFBufferSize := 300 * 1000;
GFMinBuffersize := 0;
GFStringQueue := nil;
GFConnected := false;
GFStreamLength := 0;
GFFileName := '';
GFMayjorType := '';
Fexitloop := false;
GFExit := false;
// create the Filter Pin without Stream (blank pin)
CreateFilterPin(TStreamAdapter.Create(TMemoryStream.Create, soOwned), false);
end;


constructor TAsyncEx.CreateFromFactory(Factory: TBCClassFactory;
const Controller: IUnknown);
begin

inherited CreateFromFactory(Factory, Controller);
FLock := TBCCritSec.Create;
FState := State_Stopped;
FFilestream := nil;
FFile := '';
g_threadedShoutcastStream := nil;
GFFilterCallBack := nil;
// 300kb as default
GFBufferSize := 300 * 1000;
GFMinBuffersize := 0;
GFStringQueue := nil;
GFConnected := false;
GFStreamLength := 0;
GFFileName := '';
GFMayjorType := '';
Fexitloop := false;
GFExit := false;
// create the Filter Pin without Stream (blank pin)
CreateFilterPin(TStreamAdapter.Create(TMemoryStream.Create, soOwned), false);
end;


procedure TAsyncEx.CreateFilterPin(AStream: IStream;
streamEnabled: boolean = false;
URLPin: boolean = false);
var
Hr: HRESULT;
begin

inherited Create(GCFFilterID, nil, TBCCritSec.Create, GUID_NULL, Hr);
if streamEnabled then

begin

if URLPin then

// create a URL stream pin
FPin := TStreamOutPin.Create(GCFPinID, Self, FLock, Hr,
GCFPinID, nil, true, 0, true, true)
else

// create a filestream pin
FPin := TStreamOutPin.Create(GCFPinID, Self, FLock, Hr,
GCFPinID, AStream, true, FStreamsize, true);
// maintain a ref on the pin
FPin.NonDelegatingAddRef;
// destructor flag
FPinActive := true;
end
else

begin

FPin := nil;
FPinActive := false;
end;

end;


destructor TAsyncEx.Destroy;
begin

GFExit := true;
if FFilestream <> nil then

begin

FFilestream.Destroy;
FFilestream := nil;
end;


if g_threadedShoutcastStream <> nil then

begin

g_threadedShoutcastStream.Destroy;
g_threadedShoutcastStream := nil;
end;


if GFStringQueue <> nil then

begin

GFStringQueue.Destroy;
GFStringQueue := nil;
end;

// fPin := nil;
FLock.Destroy;
end;


function TAsyncEx.GetOutPin: IPin;
begin

if FPin <> nil then

Result := FPin;
end;


function TAsyncEx.GetPin(n: Integer): TBCBasePin;
begin

if n = 0 then

Result := FPin
else

Result := nil;
end;


function TAsyncEx.GetPinCount: Integer;
begin

if FPin <> nil then

Result := 1
else

Result := 0;
end;



function TAsyncEx.NonDelegatingRelease: Integer;
begin

Result := inherited NonDelegatingRelease;
if Result = 1 then

if Result = 1 then

// the pin has a ref on us, &quot;
if FPin <> nil&quot;
then
release it
if FPin <> nil then
begin

if FFilestream <> nil then

begin

FFilestream.Destroy;
FFilestream := nil;
end;

TStreamOutPin(FPin).FreeAllObjects;
FPin.NonDelegatingRelease;
// In dsplayer 0.74 beta asyncex it
// this line commented out :(
FPin := nil;
end;

end;


// helper functions

function TAsyncEx.ParseUrl(URL: string;
out Host: string;
out Port: string;
out Location: string): boolean;
var
Pos1: integer;
Pos2: integer;
Temp: string;
begin

result := false;
if length(URL) = 0 then

exit;
// check for http string
Pos1 := pos('http://', URL);
if Pos1 = 0 then

exit;
result := true;
Temp := copy(URL, Pos1 + length('http://'), length(URL) - Pos1);
// look for port offset
Pos1 := pos(':', Temp);
// check if a port is given
if Pos1 = 0 then

begin

// no port.. , set def. port and location
Host := Temp;
Port := '80';
Location := '/';
exit;
end;

Host := copy(Temp, 1, Pos1 - 1);
// look for location offset
Pos2 := pos('/', Temp);
// check if location is given
if Pos2 = 0 then

begin

// no location.. , set def. location
Temp := copy(Temp, Pos1 + 1, length(Temp) - Pos1);
Port := Temp;
Location := '/';
exit;
end;

Port := copy(Temp, Pos1 + 1, Pos2 - Pos1 - 1);
Location := copy(Temp, Pos2, length(Temp) - Pos2 + 1);
end;


initialization
TBCClassFactory.CreateFilter(TAsyncEx, GCFFilterID, CLSID_AsyncEx,
CLSID_LegacyAmFilterCategory, MERIT_DO_NOT_USE, 1, @Pins);

end.
 
AsyncReader.pas

unit AsyncReader;

(*********************************************************************
* The contents of this file are used with permission, subject to *
* the Mozilla Public License Version 1.1 (the &quot;License&quot;);
you may *
* not use this file except in compliance with the License. You may *
* obtain a copy of the License at *
* http://www.mozilla.org/MPL/MPL-1.1.html *
* *
* Software distributed under the License is distributed on an *
* &quot;AS IS&quot;
basis, WITHOUT WARRANTY OF ANY KIND, either express or *
* implied. See the License for the specific language governing *
* rights and limitations under the License. *
* *
* (C) 2004 Martin Offenwanger: coder@dsplayer.de *
*********************************************************************)
{
@author(Martin Offenwanger: coder@dsplayer.de)
@created(Apr 22, 2004)
@lastmod(Sep 09, 2004)
}

interface

uses
ActiveX, Classes, DirectShow9, BaseClass, Windows, Queue, Config, Forms,
ShoutCastStream, SysUtils, Dialogs, ExtCtrls;

type
TAsyncIO = class(TInterfacedObject, IAsyncReader)
private
FStream: IStream;
FStop,
FWaiting,
FFlushing,
FFwdStream: boolean;
FReaderLock,
FListsLock: TBCCritSec;
FWorkList,
FDoneList: TQueue;
FWorkEvent,
FDoneEvent,
FAllDoneEv: TBCAMEvent;
FOutCount: Longint;
FStrmSize: Int64;
FThread: TThread;
FURLMode: boolean;
FMediaControl: IMediaControl;
{ the pause and run commands called with FMediaControl in Syncread
must called via a timer, otherwise ondestroy in unit filter won't called }
FTimerPlay: TTimer;
FTimerPause: TTimer;
procedure OnTimerPlay(Sender: TObject);
procedure OnTimerPause(Sender: TObject);
procedure PutDoneItem(AItem: PAsyncRequest);
function GetDoneItem: PAsyncRequest;
function PutWorkItem(AItem: PAsyncRequest): HRESULT;
function GetWorkItem: PAsyncRequest;
function SetPosition(const APos: Int64): HResult;
procedure InitStreamLen;
function SetStreamPos(const APos: Int64): HResult;
function GetStreamPos: Int64;
function CreateRequest(llPos: LONGLONG;
lLength: Integer;
bAligned: BOOL;
pBuffer: Pointer;
pContext: Pointer;
dwUser: DWORD): PAsyncRequest;
procedure CompleteRequest(Req: PAsyncRequest);
function InitAllocator(out Alloc: IMemAllocator): HRESULT;
virtual;
functiondo
Request(llPos: LONGLONG;
lLength: Longint;
bAligned: BOOL;
pBuffer: Pointer;
pContext: Pointer;
dwUser: DWORD): HResult;
functiondo
WaitForNext(dwTimeout: DWORD;
var ppContext: Pointer;
var pdwUser: DWORD;
var pcbActual: Longint): HRESULT;
protected
// IAsyncReader methods
function RequestAllocator(pPreferred: IMemAllocator;
pProps: PAllocatorProperties;
out ppActual: IMemAllocator): HResult;
stdcall;
function Request(pSample: IMediaSample;
dwUser: DWORD): HResult;
stdcall;
function WaitForNext(dwTimeout: DWORD;
out ppSample: IMediaSample;
out pdwUser: DWORD): HResult;
stdcall;
function SyncReadAligned(pSample: IMediaSample): HResult;
stdcall;
function SyncRead(llPosition: int64;
lLength: Longint;
pBuffer: Pbyte): HResult;
stdcall;
function Length(out pTotal, pAvailable: int64): HResult;
stdcall;
public
constructor Create(AStream: IStream;
FwdOnly: boolean = false;
const StreamSize: Int64 = 0;
URLMode: boolean = false);
// calling the destructor causes crashes
destructor Destroy;
override;
// we use this function to detroy memeber objects
procedure FreeAllObjects;
// the graph object for full control during buffering URL stream
procedure SetActiveGraph(var FilterGraph: IFilterGraph);
procedure Addref;
procedure Release;
procedure Process;
// IAsyncReader methods
function begin
Flush: HRESULT;
stdcall;
function EndFlush: HRESULT;
stdcall;
// FURLMode methods
procedure Connect(Adress: string;
Port: string;
Location: string;
MetaData: boolean);
end;


implementation

uses WorkerThread, filter;

procedure TAsyncIO.setActiveGraph(var FilterGraph: IFilterGraph);
begin

// In URlmode we need to control the Graph during buffering
if (FURLMode) and (FMediaControl = nil) then

begin

FilterGraph.QueryInterface(IID_IMediaControl, FMediaControl);
FTimerPlay := TTimer.Create(nil);
FTimerPlay.Enabled := false;
FTimerPlay.Interval := 1;
// makes shure that run is always called after pause
FTimerPlay.OnTimer := OnTimerPlay;
FTimerPause := TTimer.Create(nil);
FTimerPause.Enabled := false;
FTimerPause.Interval := 1;
FTimerPause.OnTimer := OnTimerPause;
end;

end;


procedure TAsyncIO.Connect(Adress: string;
Port: string;
Location: string;
MetaData: boolean);
begin

GFExit := false;
g_threadedShoutCastStream := TThreadedShoutcastStream.Create(Adress, Port,
Location, MetaData);
end;


procedure TAsyncIO.Release;
begin

FreeAllObjects;
end;


procedure TAsyncIO.Addref;
begin

_AddRef;
end;


constructor TAsyncIO.Create(AStream: IStream;
FwdOnly: boolean = false;
const StreamSize: Int64 = 0;
URLMode: boolean = false);
begin

inherited Create;
FTimerPlay := nil;
if g_threadedShoutCastStream <> nil then

begin

g_threadedShoutCastStream.Destroy;
g_threadedShoutCastStream := nil;
end;

FURLMode := URLMode;
FStream := AStream;
FListsLock := TBCCritSec.Create;
FReaderLock := TBCCritSec.Create;
FWorkList := TQueue.Create;
FDoneList := TQueue.Create;
FWorkEvent := TBCAMEvent.Create(true);
FDoneEvent := TBCAMEvent.Create(true);
FAllDoneEv := TBCAMEvent.Create(true);
FFwdStream := FwdOnly;
FStrmSize := StreamSize;
FWorkEvent.Reset;
FThread := TWorkThread.Create(Self);
FThread.Resume;
end;


procedure TAsyncIO.FreeAllObjects;
var
Req: PAsyncRequest;
begin

if g_threadedShoutCastStream <> nil then

begin

g_threadedShoutCastStream.Destroy;
g_threadedShoutCastStream := nil;
end;

if GFStringQueue <> nil then

begin

GFStringQueue.destroy;
GFStringQueue := nil;
end;

FStop := true;
FThread.Terminate;
FWorkEvent.SetEv;
FThread.WaitFor;
FThread.Free;
Req := GetDoneItem;
while Req <> nildo

begin

Dispose(Req);
Req := GetDoneItem;
end;

// FStream._Release;
FReaderLock.Free;
FListsLock.Free;
FWorkList.Free;
FDoneList.Free;
FWorkEvent.Free;
FDoneEvent.Free;
FAllDoneEv.Free;
FTimerPlay.Free;
FTimerPause.Free;
end;


destructor TAsyncIO.Destroy;
var
Req: PAsyncRequest;
begin

GFExit := true;
FStop := true;
FThread.Terminate;
FWorkEvent.SetEv;
FThread.WaitFor;
FThread.Free;
Req := GetDoneItem;
while Req <> nildo

begin

Dispose(Req);
Req := GetDoneItem;
end;

FStream := nil;
FReaderLock.Free;
FListsLock.Free;
FWorkList.Free;
FDoneList.Free;
FWorkEvent.Free;
FDoneEvent.Free;
FAllDoneEv.Free;
inherited destroy;
end;


function TAsyncIO.begin
Flush: HRESULT;
var
Req: PAsyncRequest;
begin

// if FMediaControl <> nil then

GFExit := true;
// Application.HandleMessage;
{ need to nil here IMediaControl,
if not, the destructor in TFilter will not executed }
FListsLock.Lock;
FMediaControl := nil;
Result := S_OK;
// we nil here and in the filter destructor
if g_threadedShoutCastStream <> nil then

begin

g_threadedShoutCastStream.Destroy;
g_threadedShoutCastStream := nil;
end;

if GFStringQueue <> nil then

begin

GFStringQueue.destroy;
GFStringQueue := nil;
end;

try
FFlushing := true;
Req := GetWorkItem;
while Req <> nildo

begin

PutDoneItem(Req);
Req := GetWorkItem;
end;

if FOutCount > 0 then

begin

Assert(not FWaiting);
FAllDoneEv.Reset;
FWaiting := true;
end
else

begin

FDoneEvent.SetEv;
FWorkEvent.SetEv;
end;

finally
FListsLock.UnLock;
end;

// Assert(FWaiting);
while FWaitingdo

begin

FAllDoneEv.Wait();
FListsLock.Lock;
try
if FOutCount = 0 then

begin

FWaiting := false;
FDoneEvent.SetEv;
end;

finally
FListsLock.UnLock;
end;

end;

end;


function TAsyncIO.EndFlush: HRESULT;
begin

GFExit := true;
FListsLock.Lock;
FFlushing := false;
Assert(not FWaiting);

if FDoneList.Count > 0 then

FDoneEvent.SetEv
else

FDoneEvent.Reset;

Result := S_OK;
FListsLock.UnLock;
end;


procedure TAsyncIO.Process;
var
Req: PAsyncRequest;
begin

while truedo

begin

FWorkEvent.Wait;
FListsLock.Lock;
Req := GetWorkItem;
if Req <> nil then

Inc(FOutCount);
FListsLock.UnLock;

if Req <> nil then

begin

CompleteRequest(Req);
FListsLock.Lock;
PutDoneItem(Req);
Dec(FOutCount);
if (FOutCount = 0) and FWaiting then

FAllDoneEv.SetEv;
FListsLock.UnLock;
end;

if FStop then

break;
end;

end;


function TAsyncIO.DoRequest(
llPos: LONGLONG;
lLength: Integer;
bAligned: BOOL;
pBuffer,
pContext: Pointer;
dwUser: DWORD): HResult;
var
Req: PAsyncRequest;
begin

Req := CreateRequest(llPos, lLength, bAligned, pBuffer, pContext, dwUser);
Result := PutWorkItem(Req);
if not Succeeded(Result) then

Dispose(Req);
end;


function TAsyncIO.DoWaitForNext(dwTimeout: DWORD;
var ppContext: Pointer;
var pdwUser: DWORD;
var pcbActual: Integer): HRESULT;
var
Req: PAsyncRequest;
begin

Result := S_OK;
ppContext := nil;
pdwUser := 0;
pcbActual := 0;
while truedo

begin

if (not FDoneEvent.Wait(dwTimeout)) then

begin

Result := VFW_E_TIMEOUT;
Break;
end;

Req := GetDoneItem;
if Req <> nil then

begin

ppContext := Req.FContext;
pdwUser := Req.FUser;
pcbActual := Req.FLength;
Result := Req.Fhr;
Dispose(Req);
Break;
end
else

begin

FListsLock.Lock;
try
if FFlushing {and not FWaiting} then

begin

Result := VFW_E_WRONG_STATE;
Break;
end;

finally
FListsLock.UnLock;
end;

end;

end;

end;


procedure TAsyncIO.OnTimerPlay(Sender: TObject);
begin

if FMediaControl <> nil then

FMediaControl.Run;
FTimerPlay.Enabled := false;
end;


procedure TAsyncIO.OnTimerPause(Sender: TObject);
begin

if FMediaControl <> nil then

FMediaControl.Pause;
FTimerPause.Enabled := false;
end;


function TAsyncIO.SyncRead(llPosition: int64;
lLength: Longint;
pBuffer: Pbyte): HResult;
var
Req: PAsyncRequest;
DataWritten: boolean;
i: integer;
StringStream: TStringStream;
Buffer: string;
Tempbuffer: string;
Avdata: int64;
Application: TApplication;
Buffering: boolean;
Count: integer;
begin

// wedo
not accept a Nil buffer
if pBuffer = nil then

begin

result := E_FAIL;
exit;
end;

Result := S_OK;
// the URL buffer control for Dirctshow is added here
// buffering during the playback
if FURLMode then

begin

// the min. buffersize must be equal to the requested length
if GFBufferSize < lLength then

GFBufferSize := lLength;
// Mpeg1 splitter requests same samples during connection process and
// after starting the graph.
StringStream := nil;
GFStreamPos := llPosition;
DataWritten := false;
Buffer := '';
Tempbuffer := '';
Avdata := 0;
Buffering := false;
Count := 0;
Application := TApplication.Create(nil);
if not GFConnected then

begin

if assigned(GFFilterCallBack) then

GFFilterCallBack.AsyncExFilterState(false, false, true, false, 0);

// since XP ServicePack2 rc2 the mpeg splitter requests a end sample
// of the stream during pin connection process,
// we skip this sample because we can't send it
if (llPosition > (GCFInt64max - lLength - 2)) then

begin

result := E_FAIL;
exit;
end;

i := 0;
if GFStringQueue = nil then

begin

result := E_FAIL;
exit;
end;

while not Datawrittendo

begin

if GFStringQueue <> nil then

Count := GFStringQueue.getcount;
if ((GFExit) or (GFStringQueue = nil) or (Count <= i)) then

begin

Application.Destroy;
if g_threadedShoutCastStream <> nil then

begin

g_threadedShoutCastStream.Destroy;
g_threadedShoutCastStream := nil;
end;

if GFStringQueue <> nil then

begin

if assigned(GFFilterCallBack) then

GFFilterCallBack.AsyncExSockError('Your prebuffer is too small for the pin connection process. Raise the pebuffer!')
else

ShowMessage('TAsyncIO.SyncRead: Your prebuffer is too small for the pin connection process. Raise the prebuffer!');
GFStringQueue.Destroy;
GFStringQueue := nil;
end;

result := E_FAIL;
exit;
end;

Buffer := Buffer + GFStringQueue.getitem(i);
inc(i);
if (llPosition + lLength <= Avdata) then

begin

StringStream := TStringStream.Create(Buffer);
StringStream.Position := llPosition;
Result := StringStream.Read(pBuffer^, lLength);
freeandnil(StringStream);
break;
end
else

Avdata := system.length(Buffer);
end;

end
else

begin

if assigned(GFFilterCallBack) then

GFFilterCallBack.AsyncExFilterState(false, false, false, true, 0);
while not Datawrittendo

begin

// we need to free some cpu time for other processes -> sleep(1)
Sleep(1);
if GFExit then

begin

result := E_FAIL;
Application.destroy;
if GFStringQueue <> nil then

begin

GFStringQueue.Destroy;
GFStringQueue := nil;
end;

exit;
end;

while not Bufferingdo

begin

// we need to free some cpu time for other processes -> sleep(1)
Sleep(1);
if GFExit then

begin

result := E_FAIL;
Application.destroy;
if GFStringQueue <> nil then

begin

GFStringQueue.Destroy;
GFStringQueue := nil;
end;

exit;
end;

Application.ProcessMessages;
// we needed to process the onsock read events
// during waiting for the data
while (llength > Avdata)do

begin

// we need to free some cpu time for other processes -> sleep(1)
Sleep(1);
if GFExit then

begin

result := E_FAIL;
Application.destroy;
if GFStringQueue <> nil then

begin

GFStringQueue.Destroy;
GFStringQueue := nil;
end;

exit;
end;

Application.ProcessMessages;
// we needed to process the onsock read events
// during waiting for the data
if GFStringQueue.getcount > 0 then

begin

Buffer := Buffer + GFStringQueue.pop;
Avdata := system.length(Buffer);
end
else

begin

Buffering := true;
if (FTimerPause <> nil) then

FTimerPause.Enabled := true;
break;
end;

end;

if (llength <= Avdata) then

begin

StringStream := TStringStream.Create(Buffer);
StringStream.Position := 0;
Result := StringStream.Read(pBuffer^, llength);
freeandnil(StringStream);
if (Avdata - llength > 0) then

begin

Tempbuffer := copy(Buffer, llength + 1, system.length(Buffer));
GFStringQueue.InsertItem(Tempbuffer, 0);
end;

Application.Destroy;
if assigned(GFFilterCallBack) then

GFFilterCallBack.AsyncExFilterState(false, false, false, true, 0);
// we can not call Fmediacontrol.play directly at this point,
// because destroy in uniot Filter won't called if wedo
,
// so we call the Fmediacontrol.play via a timer control
if (FTimerPlay <> nil) then

FTimerPlay.Enabled := true;
exit;
end;

end;

if assigned(GFFilterCallBack) then

GFFilterCallBack.AsyncExFilterState(true, false, false, false,
trunc((Avdata * 100) / (GFBufferSize)));
if GFStringQueue.getcount > 0 then

Buffer := Buffer + GFStringQueue.pop;
Avdata := system.length(Buffer);
if ((GFBufferSize) <= Avdata) then

begin

if assigned(GFFilterCallBack) then

GFFilterCallBack.AsyncExFilterState(true, false, false, false, 100);
StringStream := TStringStream.Create(Buffer);
StringStream.Position := 0;
Result := StringStream.Read(pBuffer^, llength);
freeandnil(StringStream);
if (Avdata - llength > 0) then

begin

Tempbuffer := copy(Buffer, llength + 1, system.length(Buffer));
GFStringQueue.InsertItem(Tempbuffer, 0);
end;

if assigned(GFFilterCallBack) then

GFFilterCallBack.AsyncExFilterState(false, false, false, true, 0);
if (FTimerPlay <> nil) then

FTimerPlay.Enabled := true;
break;
end;

end;

end;

Application.Destroy;
end
else

begin

FListsLock.Lock;
try
if FFlushing then

Result := VFW_E_WRONG_STATE
else

begin

Req := CreateRequest(llPosition, lLength, false, pBuffer, nil, 0);
CompleteRequest(Req);
Result := Req.Fhr;
Dispose(Req);
end;

finally
FListsLock.UnLock;
end;

end;

end;


function TAsyncIO.PutWorkItem(AItem: PAsyncRequest): HRESULT;
begin

FListsLock.Lock;
try
if FFlushing then

Result := VFW_E_WRONG_STATE
else

begin

FWorkList.Push(AItem);
FWorkEvent.SetEv;
Result := S_OK;
end;

finally
FListsLock.UnLock;
end;

end;


function TAsyncIO.GetWorkItem: PAsyncRequest;
begin

FListsLock.Lock;
Result := FWorkList.Pop;
if FWorkList.Count = 0 then

FWorkEvent.Reset;
FListsLock.UnLock;
end;


function TAsyncIO.GetDoneItem: PAsyncRequest;
begin

FListsLock.Lock;
Result := FDoneList.Pop;
if (FDoneList.Count = 0) and (not FFlushing or FWaiting) then

FDoneEvent.Reset;
FListsLock.UnLock;
end;


procedure TAsyncIO.PutDoneItem(AItem: PAsyncRequest);
begin

Assert(FListsLock.CritCheckIn);
FDoneList.Push(AItem);
FDoneEvent.SetEv;
end;


function TAsyncIO.Length(out pTotal, pAvailable: int64): HResult;
begin

FReaderLock.Lock;
try
if FURLMode then

begin

// we return the max int64 value
pTotal := GCFInt64max;
GFStreamLength := pTotal;
FStrmSize := pTotal;
Result := S_OK;
//VFW_S_ESTIMATED;
end
else

begin

if FStrmSize = 0 then

InitStreamLen;
pTotal := FStrmSize;
GFStreamLength := FStrmSize;
pAvailable := pTotal;
Result := S_OK;
exit;
end;

finally
FReaderLock.UnLock;
end;

end;


function TAsyncIO.SetPosition(const APos: Int64): HResult;
var
CPos: Int64;
begin

FReaderLock.Lock;
Result := S_OK;
try
if FStrmSize = 0 then

InitStreamLen;
CPos := GetStreamPos;
if not FFwdStream then

try
if CPos <> APos then

Result := SetStreamPos(APos);
except
//sometimes it's not working
//try from the begin
ing
Result := S_FALSE;
end
else

begin

try
if Apos <> CPos then

begin

if APos < CPos then

SetStreamPos(0);
Result := SetStreamPos(APos);
end;

except
Result := S_FALSE;
end;

end;

finally
FReaderLock.UnLock;
end;

end;


procedure TAsyncIO.InitStreamLen;
begin

if not FFwdStream then

try
FFwdStream := FStream.Seek(0, STREAM_SEEK_END, FStrmSize) <> S_OK;
except
FStrmSize := 0;
FFwdStream := true;
end;

if FFwdStream then

try
SetStreamPos(0);
FStrmSize := 32768;
try
while SetStreamPos(FStrmSize) = S_OKdo

FStrmSize := 2 * FStrmSize;
except
end;

FStrmSize := GetStreamPos;
SetStreamPos(0);
except
FStrmSize := 10000;
//fake
end;

end;


function TAsyncIO.GetStreamPos: Int64;
begin

FStream.Seek(0, STREAM_SEEK_CUR, Result);
GFStreamPos := Result;
end;


function TAsyncIO.SetStreamPos(const APos: Int64): HResult;
var
NewPos: Int64;
begin

Result := FStream.Seek(APos, STREAM_SEEK_SET, NewPos);
end;


procedure TAsyncIO.CompleteRequest(Req: PAsyncRequest);
var
R: integer;
begin

FReaderLock.Lock;
with Req^do

try
Fhr := SetPosition(FPos);
R := 0;
if Fhr = S_OK then

begin

Fhr := FStream.Read(FBuffer, FLength, @R);
if FLength <> R then

begin

Fhr := S_FALSE;
FLength := R;
end;

end;

finally
FReaderLock.UnLock;
end;

end;


function TAsyncIO.CreateRequest(
llPos: LONGLONG;
lLength: Integer;
bAligned: BOOL;
pBuffer,
pContext: Pointer;
dwUser: DWORD): PAsyncRequest;
begin

New(Result);
with Result^do

begin

FPos := llPos;
FAligned := bAligned;
FLength := lLength;
FBuffer := pBuffer;
FContext := pContext;
FUser := dwUser;
Fhr := VFW_E_TIMEOUT;
end;

end;


function TAsyncIO.InitAllocator(out Alloc: IMemAllocator): HRESULT;
begin

Result := CoCreateInstance(CLSID_MemoryAllocator, nil, CLSCTX_INPROC_SERVER,
IID_IMemAllocator, Alloc);
end;


function TAsyncIO.WaitForNext(dwTimeout: DWORD;
out ppSample: IMediaSample;
out pdwUser: DWORD): HResult;
var
cbActual: Longint;
begin

result :=do
WaitForNext(dwTimeout, Pointer(ppSample), pdwUser, cbActual);
end;


function TAsyncIO.RequestAllocator(pPreferred: IMemAllocator;
pProps: PAllocatorProperties;
out ppActual: IMemAllocator): HResult;
stdcall;
var
P, PA: TAllocatorProperties;
begin

P := pProps^;
P.cbAlign := 1;
if pPreferred <> nil then

begin

Result := pPreferred.SetProperties(P, PA);
if Succeeded(Result) and (P.cbAlign = PA.cbAlign) then

begin

ppActual := pPreferred;
exit;
end;

end;

InitAllocator(ppActual);
Result := ppActual.SetProperties(P, PA);
if Succeeded(Result) and (P.cbAlign = PA.cbAlign) then

begin

Result := S_OK;
exit;
end;

if Succeeded(Result) then

Result := VFW_E_BADALIGN;

ppActual := nil;
end;


function TAsyncIO.SyncReadAligned(pSample: IMediaSample): HResult;
var
T1, T2: TReferenceTime;
Start, Total: LONGLONG;
Length: Longint;
Buffer: PByte;
begin

pSample.GetTime(T1, T2);
if not FURLMode then

Self.Length(Total, Start)
else

Buffer := nil;
Start := T1 div NANOSECONDS;
Length := (T2 - T1) div NANOSECONDS;

if not FURLMode then

if Start + Length > Total then

begin

Length := Total - Start;
T2 := Total * NANOSECONDS;
pSample.SetTime(@T1, @T2);
end;


Result := pSample.GetPointer(Buffer);
if (FAILED(Result)) then

exit;

Result := SyncRead(Start, Length, Buffer);
end;


function TAsyncIO.Request(pSample: IMediaSample;
dwUser: DWORD): HResult;
var
T1, T2: TReferenceTime;
Start, Total: LONGLONG;
Length: Longint;
Buffer: PByte;
begin

pSample.GetTime(T1, T2);
self.Length(Total, Start);
Start := T1 div NANOSECONDS;
Length := (T2 - T1) div NANOSECONDS;

if Start + Length > Total then

begin

Length := Total - Start;
T2 := Total * NANOSECONDS;
pSample.SetTime(@T1, @T2);
end;


Result := pSample.GetPointer(Buffer);
if (FAILED(Result)) then

exit;

Result :=do
Request(Start, Length,
false, Buffer, Pointer(pSample), dwUser);
end;


end.
 
StreamOutPin.pas

unit StreamOutPin;

(*********************************************************************
* The contents of this file are used with permission, subject to *
* the Mozilla Public License Version 1.1 (the &quot;License&quot;);
you may *
* not use this file except in compliance with the License. You may *
* obtain a copy of the License at *
* http://www.mozilla.org/MPL/MPL-1.1.html *
* *
* Software distributed under the License is distributed on an *
* &quot;AS IS&quot;
basis, WITHOUT WARRANTY OF ANY KIND, either express or *
* implied. See the License for the specific language governing *
* rights and limitations under the License. *
* *
* (C) 2004 Martin Offenwanger: coder@dsplayer.de *
*********************************************************************)
{
@author(Martin Offenwanger: coder@dsplayer.de)
@created(Apr 22, 2004)
@lastmod(Sep 09, 2004)
}

interface

uses AsyncReader, BaseClass, DirectShow9, ActiveX, SysUtils;

type
TStreamOutPin = class(TBCBasePin, IAsyncReader)
private
FFilter: TBCBaseFilter;
FIO: TAsyncIO;
FURLMode: boolean;
FQueriedForAsyncReader: boolean;
protected
(*** IAsyncReader methods used as a Property ***)
property IO: TAsyncIO read FIO implements IAsyncReader;
public
constructor Create(ObjectName: string;
Filter: TBCBaseFilter;
Lock: TBCCritSec;
out hr: HRESULT;
Name: WideString;
AStream: IStream;
FwdOnly: boolean = false;
const StreamSize: Int64 = 0;
Loadstream: boolean = false;
URLMode: boolean = false);
// calling the destructor causes crashes, may a bug in BaseClasses
// or a iusse with release
destructor Destroy;
override;
// destroy all member objects of the pin with this procedure
procedure FreeAllObjects;
// the graph object for full control during buffering URL stream
procedure setActiveGraph(var f_FilterGraph: IFilterGraph);
// TBCBasePin Methods
function CheckMediaType(mt: PAMMediaType): HRESULT;
override;
function CheckConnect(Pin: IPin): HRESULT;
override;
function CompleteConnect(ReceivePin: IPin): HRESULT;
override;
function GetMediaType(Position: Integer;
out MediaType: PAMMediaType): HRESULT;
override;
function begin
Flush: HRESULT;
override;
stdcall;
function EndFlush: HRESULT;
override;
stdcall;
function NonDelegatingQueryInterface(const IID: TGUID;
out Obj): HRESULT;
override;
stdcall;
function BreakConnect: HRESULT;
override;
// URL
proceduredo
Connect(Adress: string;
Port: string;
Location: string;
MetaData: boolean);
end;


implementation

uses config;

procedure TStreamOutPin.setActiveGraph(var f_FilterGraph: IFilterGraph);
begin

FIO.setActiveGraph(f_FilterGraph);
end;


procedure TStreamOutPin.DoConnect(Adress: string;
Port: string;
Location: string;
MetaData: boolean);
begin

FIO.Connect(Adress, Port, Location, MetaData);
FURLMode := true;
end;


function TStreamOutPin.CheckMediaType(mt: PAMMediaType): HRESULT;
begin

if FURLMode then

begin

if GFStringQueue = nil then

begin

Result := S_FALSE;
exit;
end;

end;

if IsEqualGUID(mt.majortype, MEDIATYPE_Stream) then

begin

Result := S_OK;
end
else

Result := S_FALSE;
end;


constructor TStreamOutPin.Create(ObjectName: string;
Filter: TBCBaseFilter;
Lock: TBCCritSec;
out hr: HRESULT;
Name: WideString;
AStream: IStream;
FwdOnly: boolean = false;
const StreamSize: Int64 = 0;
Loadstream: boolean = false;
URLMode: boolean = false);
begin

FFilter := Filter;
GFConnected := false;
FURLMode := false;
inherited Create(ObjectName, Filter, Lock, hr, Name, PINDIR_OUTPUT);
if Loadstream then

begin

if URLMode then

FIO := TAsyncIO.Create(AStream, FwdOnly, StreamSize, true)
else

FIO := TAsyncIO.Create(AStream, FwdOnly, StreamSize);
FIO.AddRef;
end;

end;


destructor TStreamOutPin.Destroy;
begin

FIO.Release;
inherited Destroy;
end;


procedure TStreamOutPin.FreeAllObjects;
begin

FIO.freeAllObjects;
end;


function TStreamOutPin.begin
Flush: HRESULT;
begin

Result := FIO.begin
Flush;
end;


function TStreamOutPin.EndFlush: HRESULT;
begin

Result := FIO.EndFlush;
end;


function TStreamOutPin.GetMediaType(Position: Integer;
out MediaType: PAMMediaType): HRESULT;
begin

MediaType.majortype := MEDIATYPE_Stream;
Result := S_OK;
GFMayjorType := GUIDToString(MEDIATYPE_Stream);
if (Position >= 0) and (Position <= High(ProposedTypes)) then

MediaType.subtype := ProposedTypes[Position]^
else

Result := VFW_S_NO_MORE_ITEMS;

end;


function TStreamOutPin.CheckConnect(Pin: IPin): HRESULT;
begin

FQueriedForAsyncReader := false;
Result := inherited CheckConnect(Pin);
end;


function TStreamOutPin.CompleteConnect(ReceivePin: IPin): HRESULT;
begin

GFConnected := true;
if (FQueriedForAsyncReader) then

Result := inherited CompleteConnect(ReceivePin)
else

Result := VFW_E_NO_TRANSPORT;
end;


function TStreamOutPin.NonDelegatingQueryInterface(const IID: TGUID;
out Obj): HRESULT;
begin

if IsEqualGUID(IID, IID_IAsyncReader) then

FQueriedForAsyncReader := true;
Result := inherited NonDelegatingQueryInterface(IID, Obj);
end;


function TStreamOutPin.BreakConnect: HRESULT;
begin

FQueriedForAsyncReader := false;
Result := inherited BreakConnect;
end;


end.
 
config.pas

unit config;

(*********************************************************************
* The contents of this file are used with permission, subject to *
* the Mozilla Public License Version 1.1 (the &quot;License&quot;);
you may *
* not use this file except in compliance with the License. You may *
* obtain a copy of the License at *
* http://www.mozilla.org/MPL/MPL-1.1.html *
* *
* Software distributed under the License is distributed on an *
* &quot;AS IS&quot;
basis, WITHOUT WARRANTY OF ANY KIND, either express or *
* implied. See the License for the specific language governing *
* rights and limitations under the License. *
* *
* (C) 2004 Martin Offenwanger: coder@dsplayer.de *
*********************************************************************)
{
@author(Martin Offenwanger: coder@dsplayer.de)
@created(Apr 22, 2004)
@lastmod(Sep 09, 2004)
}

interface

uses DirectShow9, Classes, StringQueue, ShoutCastStream, BaseClass, ActiveX;

// Global Filter Identifiers
const
GCFInt64max = 999999999999999999;
GCFFilterID = 'AsyncEx';
GCFPinID = 'StreamOut';
// GUIDS
CLSID_AsyncEx: TGUID = '{3E0FA044-926C-42d9-BA12-EF16E980913B}';
CLSID_PropMonitor: TGUID = '{3E0FA066-929C-43d9-BA18-EF16E980913B}';
CLSID_PropPage: TGUID = '{3E0FA055-926C-43d9-BA18-EF16E980913B}';
// Interface ID'S
IID_IAsyncExControl: TGUID =
'{3E0FA056-926C-43d9-BA18-EF16E980913B}';
IID_IAsyncExCallBack: TGUID =
'{3E0FB667-956C-43d9-BA18-EF16E980913B}';
{ ogg media
Tobias Ogg Splitter implementation causes a lot of problems with the source,
so i disabled Ogg palyback for now..... }
// MEDIASUBTYPE_OGGAudio : TGUID = '{D2855FA9-61A7-4db0-B979-71F297C17A04}';

// Proposed supported SubTypes
ProposedTypes:
array[0..9] of PGuid = (
@MEDIASUBTYPE_Avi,
@MEDIASUBTYPE_AIFF,
@MEDIASUBTYPE_AU,
@MEDIASUBTYPE_DssAudio,
@MEDIASUBTYPE_DssVideo,
@MEDIASUBTYPE_MPEG1Audio,
@MEDIASUBTYPE_MPEG1System,
@MEDIASUBTYPE_MPEG1Video,
@MEDIASUBTYPE_MPEG1VideoCD,
@MEDIASUBTYPE_WAVE
);
// Stream as Majortype
PinType: TRegPinTypes =
(clsMajorType: @MEDIATYPE_Stream);

// one Pin
Pins: array[0..0] of TRegFilterPins =
((strName: GCFPinID;
bRendered: FALSE;
bOutput: TRUE;
bZero: FALSE;
bMany: FALSE;
oFilter: nil;
strConnectsToPin: nil;
nMediaTypes: 1;
lpMediaType: @PinType));

// Interfaces, that can Queried on the Filter
type
IAsyncExCallBack = interface(IUnknown)
['{3E0FB667-956C-43d9-BA18-EF16E980913B}']
function AsyncExFilterState(Buffering: LongBool;
PreBuffering: LongBool;
Connecting: LongBool;
Playing: LongBool;
BufferState: integer): HRESULT;
stdcall;
function AsyncExICYNotice(IcyItemName: PChar;
ICYItem: PChar): HRESULT;
stdcall;
function AsyncExMetaData(Title: PChar;
URL: PChar): HRESULT;
stdcall;
function AsyncExSockError(ErrString: PChar): HRESULT;
stdcall;
end;


type
IAsyncExControl = interface(IUnknown)
['{3E0FA056-926C-43d9-BA18-EF16E980913B}']
function SetLoadFromStream(Stream: IStream;
Length: int64): HRESULT;
stdcall;
function SetConnectToIp(Host: PChar;
Port: PChar;
Location: PChar;
PreBuffersize: integer;
MetaData: LongBool): HRESULT;
stdcall;
function SetConnectToURL(URL: PChar;
PreBuffersize: integer;
MetaData: LongBool): HRESULT;
stdcall;
function SetBuffersize(BufferSize: integer): HRESULT;
stdcall;
function GetBuffersize(out BufferSize: integer): HRESULT;
stdcall;
function SetRipStream(Ripstream: LongBool;
Path: PChar;
Filename: PChar): HRESULT;
stdcall;
function GetRipStream(out Ripstream: LongBool;
out FileO: PChar): HRESULT;
stdcall;
function SetCallBack(CallBack: IAsyncExCallBack): HRESULT;
stdcall;
function FreeCallback(): HRESULT;
stdcall;
function ExitAllLoops(): HRESULT;
stdcall;
end;


{ it is not the best way to use this objects as global,
but for now it works ;)
todo: move the Global Objects to private }
var
// Filter callBack
GFFilterCallBack: IAsyncExCallBack;
GFPreBufferSize: integer;
GFBufferSize: integer;
GFMinBuffersize: integer;
// external URl Stream Class
GFStringQueue: TStringQueue;
// global queue for all received data
{ we handle the queue from different classes
tofo: rewmove this global var }
// flags
GFConnected: boolean;
GFExit: boolean;
// state informations
GFStreamPos: int64;
GFStreamLength: int64;
GFFileName: string;
GFMayjorType: string;

implementation

end.
 
ICYParser.pas

unit ICYParser;

(*********************************************************************
* The contents of this file are used with permission, subject to *
* the Mozilla Public License Version 1.1 (the &quot;License&quot;);
you may *
* not use this file except in compliance with the License. You may *
* obtain a copy of the License at *
* http://www.mozilla.org/MPL/MPL-1.1.html *
* *
* Software distributed under the License is distributed on an *
* &quot;AS IS&quot;
basis, WITHOUT WARRANTY OF ANY KIND, either express or *
* implied. See the License for the specific language governing *
* rights and limitations under the License. *
* *
* (C) 2004 Martin Offenwanger: coder@dsplayer.de *
*********************************************************************)
{
@author(Martin Offenwanger: coder@dsplayer.de)
@created(Apr 22, 2004)
@lastmod(Sep 09, 2004)
}

interface

// ICY Item Names
const
ICYMetaInt = 'icy-metaint:';
ICYName = 'icy-name:';
ICYGenre = 'icy-genre:';
ICYURL = 'icy-url:';
ICYBitrate = 'icy-br:';
ICYError = 'icy-error:';

// functions return error &quot;
N/A (String)&quot;,&quot;
0 (integer)&quot;,&quot;
false (Boolean)&quot;
function GetICYItem(ICYItemName: string;
Streamheader: string): string;
function GetServerICYInt(Streamheader: string): integer;
function GetServerICYName(StreamHeader: string): string;
function GetServerICYGenre(StreamHeader: string): string;
function GetServerICYURL(StreamHeader: string): string;
function GetServerICYBitRate(StreamHeader: string): string;
function GetICYSuccessfullyConnected(StreamHeader: string;
out ErrMessage: string): boolean;

implementation

uses
SysUtils;

function GetICYSuccessfullyConnected(StreamHeader: string;
out ErrMessage: string): boolean;
var
Pos1: integer;
begin

Pos1 := Pos('200 OK', StreamHeader);
if Pos1 = 0 then

begin

ErrMessage := copy(StreamHeader, 1, length(StreamHeader));
result := false;
exit;
end;

ErrMessage := '';
result := true;
end;


function GetICYItem(ICYItemName: string;
Streamheader: string): string;
var
Pos1: integer;
ICYItem: string;
i: integer;
begin

Pos1 := Pos(ICYItemName, Streamheader);
if Pos1 = 0 then

begin

result := 'N/A';
exit;
end;

Streamheader := copy(Streamheader, Pos1 + length(ICYItemName),
length(Streamheader) - Pos1 + length(ICYItemName));
Pos1 := Pos(#13#10, Streamheader);
Streamheader := copy(Streamheader, 0, Pos1);
ICYItem := '';
for i := 1 to length(Streamheader) - 1do

if Streamheader <> ' ' then

ICYItem := ICYItem + Streamheader;
result := ICYItem;
end;


function GetServerICYName(StreamHeader: string): string;
begin

result := GetICYItem(ICYName, StreamHeader);
end;


function GetServerICYGenre(StreamHeader: string): string;
begin

result := GetICYItem(ICYGenre, StreamHeader);
end;


function GetServerICYURL(StreamHeader: string): string;
begin

result := GetICYItem(ICYURL, StreamHeader);
end;


function GetServerICYBitRate(StreamHeader: string): string;
begin

result := GetICYItem(ICYBitrate, StreamHeader);
end;


function GetServerICYInt(Streamheader: string): integer;
var
ResultS: string;
begin

ResultS := GetICYItem(ICYMetaInt, Streamheader);
if ResultS = 'N/A' then

begin

Result := 0;
exit;
end;

Result := strtoint(ResultS);
end;


end.
 
Queue.pas

unit Queue;

(*********************************************************************
* The contents of this file are used with permission, subject to *
* the Mozilla Public License Version 1.1 (the &quot;License&quot;);
you may *
* not use this file except in compliance with the License. You may *
* obtain a copy of the License at *
* http://www.mozilla.org/MPL/MPL-1.1.html *
* *
* Software distributed under the License is distributed on an *
* &quot;AS IS&quot;
basis, WITHOUT WARRANTY OF ANY KIND, either express or *
* implied. See the License for the specific language governing *
* rights and limitations under the License. *
* *
* (C) 2004 Martin Offenwanger: coder@dsplayer.de *
*********************************************************************)
{
@author(Martin Offenwanger: coder@dsplayer.de)
@created(Apr 22, 2004)
@lastmod(Sep 09, 2004)
}

interface

uses
Classes, Windows;

type
PAsyncRequest = ^TAsyncRequest;
TAsyncRequest = record
FPos: LONGLONG;
FAligned: BOOL;
FLength: Integer;
FBuffer: Pointer;
FContext: Pointer;
FUser: DWORD;
Fhr: HRESULT;
end;


type
TQueue = class(TList)
public
constructor Create;
destructor Destroy;
override;
procedure Push(AItem: Pointer);
function Pop: Pointer;
end;


implementation

constructor TQueue.Create;
begin

inherited Create;
end;


destructor TQueue.Destroy;
begin

inherited Destroy;
end;


function TQueue.Pop: Pointer;
begin

if Count > 0 then

begin

Result := Items[0];
Delete(0);
end
else

Result := nil;
end;


procedure TQueue.Push(AItem: Pointer);
begin

Add(AItem);
end;


end.
 
ShoutCastStream.pas

unit ShoutCastStream;

(*********************************************************************
* The contents of this file are used with permission, subject to *
* the Mozilla Public License Version 1.1 (the &quot;License&quot;);
you may *
* not use this file except in compliance with the License. You may *
* obtain a copy of the License at *
* http://www.mozilla.org/MPL/MPL-1.1.html *
* *
* Software distributed under the License is distributed on an *
* &quot;AS IS&quot;
basis, WITHOUT WARRANTY OF ANY KIND, either express or *
* implied. See the License for the specific language governing *
* rights and limitations under the License. *
* *
* (C) 2004 Martin Offenwanger: coder@dsplayer.de *
*********************************************************************)
{
@author(Martin Offenwanger: coder@dsplayer.de)
@created(Apr 22, 2004)
@lastmod(Sep 09, 2004)
}

interface

uses
Windows, Controls, Sock, Forms, SysUtils, BaseClass, Dialogs,
WinSock, ICYParser;

{ how to use tip:
we are running a async. winsock
The Winsock sends its event handling trough the Windows Message Queue
You should create this class in a Thread and/or use
TApplication.processmessages in external buffering loops }

type
TShoutcastStream = class
public
FApplication: TApplication;
constructor Create;
destructor Destroy;
override;
procedure SetConnectToIp(Adress: string;
Port: string;
Location: string;
Meta: boolean);
function SetRipStream(RipStream: boolean;
Path: string;
FileO: string): HRESULT;
function GetRipStream(out RipStream: boolean;
out Path: string): HRESULT;
private
FLock: TBCCritSec;
FSock: TSock;
// Winsock class
{ sock message receiver
( we are running a async winsock.
&quot;
requires a TForm listener &quot;
) }
FReceiveForm: TForm;
FLocation: string;
// host Location (Path only)
FHeaderFound: boolean;
// header flag
FICYHeader: string;
// the header itself
// ripper feature Objects
FPath: string;
// used filePath
FFile: string;
// Location and filename
FFileNoMetaData: string;
// file to record in NoMetaData Mode
FFileObject: TextFile;
// FileObject
FRipStream: boolean;
// ripper state flag
FFileCreated: boolean;
// file state flag
// Metadata count
FMetaInterval: integer;
FMetaCount: integer;
FMetaStartFound: boolean;
FTempSave: string;
FOutOfSync: boolean;
FMetadataEnabled: boolean;
// connect message receiver
procedure OnSockConnect(Sender: TObject);
// read message receiver
procedure OnSockRead(Sender: TObject;
Count: Integer);
procedure OnSockInfo(Sender: TObject;
SocketInfo: TSocketInfo;
Msg: string);
// metadata format: &quot;StreamTitle='content;StreamURL='content';&quot;
function getStreamTitle(Metadata: string): string;
function getStreamURl(Metadata: string): string;
// ripper
procedure createNewFileIfNeeded(Metadata: string);
procedure createFileNoMeataInt(FileO: string);
protected
end;


implementation

uses config;

function TShoutcastStream.GetRipStream(out RipStream: boolean;
out Path: string): HRESULT;
begin

FLock.Lock;
RipStream := FRipStream;
Path := copy(FPath, 1, length(FPath));
RESULT := S_OK;
FLock.UnLock;
end;


function TShoutcastStream.SetRipStream(RipStream: boolean;
Path: string;
FileO: string): HRESULT;
begin

FLock.Lock;
FRipStream := RipStream;
FPath := copy(Path, 1, length(Path));
if not RipStream then

FFile := '';
FFileNoMetaData := copy(FileO, 1, length(FileO));
RESULT := S_OK;
FLock.UnLock;
end;


function TShoutcastStream.GetStreamTitle(Metadata: string): string;
var
Pos1: integer;
Temp: string;
begin

Pos1 := Pos('''', Metadata);
Temp := copy(Metadata, Pos1 + 1, length(Metadata) - Pos1 - 1);
Pos1 := Pos('''', Temp);
Result := copy(Temp, 1, Pos1 - 1);
end;


function TShoutcastStream.GetStreamURl(Metadata: string): string;
var
Pos1: integer;
Temp: string;
begin

// search for the first offset
Pos1 := Pos(';', Metadata);
Temp := copy(Metadata, Pos1 + 1, length(Metadata) - Pos1 - 1);
Result := getStreamTitle(Temp);
end;


procedure TShoutcastStream.createNewFileIfNeeded(metadata: string);
var
Title: string;
Pos1: integer;
begin

Title := getStreamTitle(Metadata);
if (Title <> FFile) then

GFFileName := Title + '.mp3';
if FRipStream then

begin

if (Title <> FFile) then

begin

FFile := Title;
if FPath <> '' then

SetCurrentDir(FPath);
if FFileCreated then

CloseFile(FFileObject);
// check if the file name is supported ( //:*?&quot;<>| )
Pos1 := Pos('/', Title);
if Pos1 <> 0 then

Title := copy(Title, 1, Pos1 - 1);
Pos1 := Pos('/', Title);
if Pos1 <> 0 then

Title := copy(Title, 1, Pos1 - 1);
Pos1 := Pos(':', Title);
if Pos1 <> 0 then

Title := copy(Title, 1, Pos1 - 1);
Pos1 := Pos('*', Title);
if Pos1 <> 0 then

Title := copy(Title, 1, Pos1 - 1);
Pos1 := Pos('?', Title);
if Pos1 <> 0 then

Title := copy(Title, 1, Pos1 - 1);
Pos1 := Pos('&quot;', Title);
if Pos1 <> 0 then

Title := copy(Title, 1, Pos1 - 1);
Pos1 := Pos('<', Title);
if Pos1 <> 0 then

Title := copy(Title, 1, Pos1 - 1);
Pos1 := Pos('>', Title);
if Pos1 <> 0 then

Title := copy(Title, 1, Pos1 - 1);
Pos1 := Pos('|', Title);
if Pos1 <> 0 then

Title := copy(Title, 1, Pos1 - 1);
// rewrite existing files to reduce overhead :/
if FPath <> '' then

SetCurrentDir(FPath);
if FileExists(Title + '.mp3') then

DeleteFile(Title + '.mp3');
try
if FPath <> '' then

SetCurrentDir(FPath);
AssignFile(FFileObject, Title + '.mp3');
ReWrite(FFileObject);
FFileCreated := true;
except
ShowMessage('A bug has been found in ASyncEx Filter' +
'please post the folowing line to: coder@dsplayer.de : ' +
Title + '.mp3');
end;

end;

end
else

begin

if FFileCreated then

CloseFile(FFileObject);
FFileCreated := false;
end;

end;


procedure TShoutcastStream.createFileNoMeataInt(FileO: string);
begin

if FRipStream then

begin

if not FFileCreated then

begin

if FileExists(FileO) then

// we rewrite existing files to reduce overhead :/
DeleteFile(FileO);
try
if FPath <> '' then

SetCurrentDir(FPath);
if FFileCreated then

CloseFile(FFileObject);
AssignFile(FFileObject, FileO);
ReWrite(FFileObject);
FFileCreated := true;
except
ShowMessage('A bug has been found in DSPlayer ASync.Source' +
'please post the folowing line to: coder@dsplayer.de : ' +
FileO);
end;

end;

end
else

begin

if FFileCreated then

CloseFile(FFileObject);
FFileCreated := false;
end;

end;


procedure TShoutcastStream.OnSockRead(Sender: TObject;
Count: Integer);
var
Temp: string;
Temp2: string;
MyPos: integer;
Subi: integer;
Pos1, Pos2: integer;
MetaString: string;
LengthO: byte;
CharO: char;
TempSave: string;
MetaTitle: string;
MetaUrl: string;
ErrMsg: string;
begin

{ -> This section includes the streamparser,fileripper and buffer abilties <-
todo: - code cleaning
- better helper functions or helper classes
}
try
FLock.Lock;
Temp := FSock.Receive;
// get the received data from winsock buffer
// get the end of url header count
MyPos := 0;
if not FHeaderFound then

begin

FTempSave := FTempSave + Temp;
Temp := FTempSave;
MyPos := Pos(#13#10#13#10, Temp);
end;

if MyPos <> 0 then

begin

// cut the header and save it into FICYHeader
Temp2 := Temp;
Temp := Copy(Temp, MyPos + 4, StrLen(@MyPos) - 4);
// get mp3 data
Temp2 := Copy(Temp2, 0, MyPos + 2);
// get the URL header
FICYHeader := Temp2;
// save the URL header
// get the Metadata count:
FMetaInterval := GetServerICYInt(Temp2);
// header callback
if GFFilterCallBack <> nil then

begin

if not GetICYSuccessfullyConnected(FICYHeader, ErrMsg) then

begin

GFFilterCallBack.AsyncExICYNotice(ICYError, PChar(ErrMsg));
end;

// try to get icy informations
GFFilterCallBack.AsyncExICYNotice(PChar(ICYName),
PChar(GetServerICYName(FICYHeader)));
GFFilterCallBack.AsyncExICYNotice(PChar(ICYGenre),
PChar(GetServerICYGenre(FICYHeader)));
GFFilterCallBack.AsyncExICYNotice(PChar(ICYURL),
PChar(GetServerICYURL(FICYHeader)));
GFFilterCallBack.AsyncExICYNotice(PChar(ICYBitrate),
PChar(GetServerICYBitRate(FICYHeader)));
end
else

begin

if not GetICYSuccessfullyConnected(FICYHeader, ErrMsg) then

showmessage('Can not receive the Stream.'#13#10#13#10 +
'Reason:'#13#10 + ErrMsg);
end;

// push the mp3 data to queue
if GFStringQueue <> nil then

GFStringQueue.Push(Temp);
if (not FMetadataEnabled) and (FMetaInterval = 0) then

begin

createFileNoMeataInt(FFileNoMetaData);
if FPath <> '' then

SetCurrentDir(FPath);
if FFileCreated and FRipStream then

Write(FFileObject, Temp);
end;

if FMetaInterval <> 0 then

begin

FMetaCount := length(Temp);
end;

// set header found state flag
FHeaderFound := true;
FMetaStartFound := false;
FTempSave := '';
FLock.UnLock;
exit;
end;


// if found and cutted the URLheader start to add mp3 data to the queue
if FHeaderFound then

begin

if FTempSave <> '' then

begin
// completion of metadatablock isdo
ne here
TempSave := copy(FTempSave, 1, length(FTempSave));
Temp := copy(TempSave, 1, length(TempSave)) + copy(Temp, 1,
length(Temp));
FTempSave := '';
end;

Pos1 := Pos('StreamTitle', Temp);
if Pos1 <> 0 then

begin

CharO := (copy(Temp, Pos1 - 1, 1))[1];
LengthO := ((byte(CharO)) * 16);
if length(Temp) < Pos1 + LengthO - 1 then

begin

// found a incomlete metadata block
FTempSave := FTempSave + copy(Temp, 1, length(Temp));
FLock.UnLock;
exit;
end;

end;

if FMetaInterval <> 0 then

begin

FMetaCount := FMetaCount + length(Temp);
{ some servers send the first Metatag at a unspezified point! ,
so try to get the first sended Meta Info, and count the received
mp3 data }
Pos1 := Pos('StreamTitle', Temp);
if Pos1 <> 0 then

begin

Pos2 := length(Temp) - Pos1;
CharO := (copy(Temp, Pos1 - 1, 1))[1];
LengthO := ((byte(CharO)) * 16);
MetaString := copy(Temp, Pos1, LengthO - 1);
if MetaString <> '' then

begin

// MetaData Callback
if GFFilterCallBack <> nil then

begin

// parse stream Title &amp;
streamUrl
MetaTitle := getStreamTitle(MetaString);
MetaUrl := getStreamURl(MetaString);
// Stream MetaData Callback (parsed)
if length(MetaTitle) = 0 then

MetaTitle := 'N/A';
if length(MetaUrl) = 0 then

MetaUrl := 'N/A';
GFFilterCallBack.AsyncExMetaData(PChar(MetaTitle),
PChar(MetaUrl));
end;

FOutOfSync := false;
createNewFileIfNeeded(MetaString);
end;

// set the remaining data
Temp2 := copy(Temp, 0, Pos1 - 2);
Temp := Temp2 + copy(Temp, Pos1 - 1 + LengthO + 1, Pos2 - LengthO +
1);
// calculate the remaining mp3 data
FMetaCount := Pos2 - LengthO + 1;
if (GFStringQueue <> nil) and (not FOutOfSync) then

GFStringQueue.Push(Temp);
// push the received mp3 data to the queue
if FPath <> '' then

SetCurrentDir(FPath);
if (FRipStream and FFileCreated and not FOutOfSync) then

Write(FFileObject, Temp);
FLock.UnLock;
exit;
end;

if FMetaCount > FMetaInterval then

begin

// calculate the start and end of the meta data in current block
Subi := FMetaCount - FMetaInterval;
Pos1 := length(Temp) - Subi + 1;
// get the length of the MetaData
CharO := (copy(Temp, Pos1, 1))[1];
LengthO := ((byte(CharO)) * 16);
if length(Temp) < Pos1 + LengthO - 1 then

begin

// found a incomlete metadata block
FTempSave := FTempSave + copy(Temp, 1, length(Temp));
FLock.UnLock;
exit;
end;

if LengthO <> 0 then

if Pos('Stream', MetaString) = 0 then

begin

// Server is out of Sync.!
if GFFilterCallBack <> nil then

// >ToDO: error callback
GFFilterCallBack.AsyncExMetaData('Server is out of sync, trying to resync', 'Server is out of sync, trying to resync');
FOutOfSync := true;
FLock.UnLock;
exit;
end;

MetaString := copy(Temp, Pos1, LengthO);
if MetaString <> '' then

begin
// a metastring has been found
if GFFilterCallBack <> nil then

begin

// parse stream Title &amp;
streamUrl
MetaTitle := getStreamTitle(MetaString);
MetaUrl := getStreamURl(MetaString);
// Stream MetaData Callback (parsed)
if length(MetaTitle) = 0 then

MetaTitle := 'N/A';
if length(MetaUrl) = 0 then

MetaUrl := 'N/A';
GFFilterCallBack.AsyncExMetaData(PChar(MetaTitle), PChar(MetaUrl));
end;

if not (FOutOfSync) then

createNewFileIfNeeded(MetaString);
end;

// set the remaining data
Temp2 := copy(Temp, 0, Pos1 - 1);
Temp := Temp2 + copy(Temp, Pos1 + LengthO + 1, Subi - LengthO - 1);
// calculate the remaining mp3 data
FMetaCount := Subi - LengthO - 1
end;

end;

if (GFStringQueue <> nil) and (not FOutOfSync) then

GFStringQueue.Push(Temp);
// pop the received mp3 data to the queue
// file ripper feature
if (not FMetadataEnabled) and (FMetaInterval = 0) then

createFileNoMeataInt(FFileNoMetaData);
if FPath <> '' then

SetCurrentDir(FPath);
if (FRipStream) and (FFileCreated) and (not FOutOfSync) then

Write(FFileObject, Temp);
end;

FLock.UnLock;
except
FLock.UnLock;
// no exception handling at present :(
// during prebuffering and during minimizing the app an exception is thrown:
// -> nil pointer acces
end;

end;


procedure TShoutcastStream.OnSockConnect(Sender: TObject);
begin

FLock.Lock;
if FMetadataEnabled then

// send the official connect string (metadata)
FSock.Send('GET ' + FLocation + ' HTTP/1.0'#13#10
+ 'User-Agent: DSPlayer'#13#10
+ 'Host: '#13#10
+ 'icy-MetaData:1'#13#10#13#10)
else

// send the official connect string (no metadata)
FSock.Send('GET ' + FLocation + ' HTTP/1.0'#13#10
+ 'User-Agent: DSPlayer'#13#10
+ 'Host: '#13#10#13#10#13#10);
FLock.UnLock;
end;


procedure TShoutcastStream.SetConnectToIP(Adress: string;
Port: string;
Location: string;
Meta: boolean);
begin

FLock.Lock;
FMetadataEnabled := Meta;
FSock.HostName := Adress;
FSock.PortName := Port;
FLocation := Location;
FSock.Connected := true;
FLock.UnLock;
end;


procedure TShoutcastStream.OnSockInfo(Sender: TObject;
SocketInfo: TSocketInfo;
Msg: string);
begin

FLock.Lock;
FApplication.ProcessMessages;
if SocketInfo = siError then

begin

GFExit := true;
// Error Handling
//..
// Somtimes when we connect to a still well connected Adress
// the sock api is a little slow and needs some time to free the
// used address. -> error WSAEADDRINUSE
if Assigned(GFFilterCallBack) then

GFFilterCallBack.AsyncExSockError(PChar(Msg))
else

ShowMessage(Msg);
end;

FLock.UnLock;
end;


constructor TShoutcastStream.Create;
begin

FMetadataEnabled := true;
FMetaCount := 0;
FMetaInterval := 0;
FFile := '';
FRipStream := false;
FFileCreated := false;
FLock := TBCCritSec.Create;
FReceiveForm := TForm.Create(nil);
FReceiveForm.Hide;
FSock := TSock.Create(FReceiveForm);
FSock.OnConnect := OnSockConnect;
FSock.OnInfo := OnSockInfo;
FSock.OnRead := OnSockRead;
FHeaderFound := false;
FOutOfSync := false;
FTempSave := '';
FFileNoMetaData := '';
FApplication := TApplication.Create(nil);
FICYHeader := 'No Header aviailble at present';
end;


destructor TShoutcastStream.Destroy;
var
Application: TApplication;
begin

FLock.Lock;
Application := TApplication.Create(nil);
FApplication.Destroy;
if FFileCreated then

CloseFile(FFileObject);
// FSock.Connected := true;
// FSock.Connected := false;
// FSock.Close;
// FSock.Destroy;
// buggy, if detroy is called Sock Adress might be still in use
FSock := nil;
Application.Destroy;
FReceiveForm.Free;
FLock.UnLock;
FLock.Free;
inherited Destroy;

end;


end.
 
Sock.pas
unit Sock;

// *****************************************************************************
// Sock.Pas (TSock)
// Freeware Windows Socket Component For Delphi &amp;
C++ Builder
// Version 1.0k, tested with Delphi 2.0, 3.0 &amp;
4.0
// Written By Tom Bradford
// Maintained By Ward van Wanrooij
// (ward@ward.nu, http://www.ward.nu)
//
// Copyright (C) 1997-2000, Beachdo
g Software, Inc.
// Copyright (C) 2000-2003, Ward van Wanrooij
// All Rights Reserved
// Latest version can be obtained at http://www.ward.nu/computer/tsock
// *****************************************************************************

interface

uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
WinSock, BaseClass;

type
TSocketInfo = (siLookUp, siConnect, siClose, siListen, siReceive, siSend,
siAccept, siError);
TSocketType = (stStream, stDatagram);
TLineBreak = (lbCRLF, lbCR, lbLF, lbSmart);

const
WM_SOCK = WM_USER + 75;
// Hopefully, Your App Won't Use This Message

type
TSock = class;
// Forward Declared For Event Types

ESockException = class(Exception);
TNotifyReadEvent = procedure(Sender: TObject;
Count: Integer) of object;
TNotifyAutoEvent = procedure(Sender: TObject;
NewSock: TSock) of object;
TNotifyInfoEvent = procedure(sender: TObject;
SocketInfo: TSocketInfo;
Msg:
string) of object;

TSock = class(TCustomControl)
private
FSockAddrIn: TSockAddrIn;
// Address Information Block
FRecvAddrIn: TSockAddrIn;
// Address Information Block For RecvFrom
FLastChar: Char;
// Last Character Read For Line-Input

FPicture: TBitmap;
// Holder For Design-Time Image
FBmp_TCP: TBitmap;
// TCP Bitmap
FBmp_UDP: TBitmap;
// UDP Bitmap
FBmp_Listen: TBitmap;
// Listening Bitmap

// Character Buffer (Most WINSOCK.DLLs Max At 32k)
// FCharBuf : Array[1..32768] Of Char;
FCharBuf: array[1..750] of Char;
// small buffer works more stable
FSocketType: TSocketType;
// Socket Type (Stream Or Datagram)
FLineBreak: TLineBreak;
// Line Break Style For Line Input
FHostName: string;
// Host Name Or IP Address
FPortName: string;
// Port Name Or Well-Known Number
FLocalPortName: string;
// Local Port Name Or Well-Known Number, Defaults To 1 (=FPortName) For Backward Compatibility
FSocket: TSocket;
// Socket Handle
FInBuffer: string;
// Input Buffer
FOutBuffer: string;
// Output Buffer For Non-Blocking
FListen: Boolean;
// Socket Listens?
FBlocking: Boolean;
//do
Blocking Calls?
FAutoAccept: Boolean;
// Automatically Accept Incomings
FConnected: Boolean;
// Are We Connected?
FBlockTime: Integer;
// How Long To Wait For Blocking Operation
FStream: TStream;
// Associated TSockStream Object
FFreeOnClose: Boolean;
// Free after closure of socket? (Non-blocking, auto-accepted sockets!)

FOnConnect: TNotifyEvent;
FOnDisconnect: TNotifyEvent;
FOnInfo: TNotifyInfoEvent;
FOnRead: TNotifyReadEvent;
FOnWrite: TNotifyEvent;
FOnAccept: TNotifyEvent;
FOnAutoAccept: TNotifyAutoEvent;

m_receiveForm: TForm;
m_lock: TBCCritSec;

// Property Set/Get Routines
procedure SetHostName(Value: string);
procedure SetPortName(Value: string);
procedure SetLocalPortName(Value: string);
function GetText: string;
procedure SetText(Value: string);
procedure SetListen(Value: Boolean);
procedure SetBlocking(Value: Boolean);
procedure SetAutoAccept(Value: Boolean);
procedure SetConnected(Value: Boolean);
function GetConnected: Boolean;
procedure SetSocket(Value: TSocket);
procedure SetSocketType(Value: TSocketType);
function GetRemoteHost: string;
function GetEOF: Boolean;

// Private Support Methods
proceduredo
Info(SocketInfo: TSocketInfo;
Msg: string);
procedure SetBitmap;
protected
// Event Handlers
procedure WMSock(var Message: TMessage);
message WM_SOCK;
procedure WMPaint(var Message: TWMPaint);
message WM_PAINT;
procedure WMSize(var Message: TWMSize);
message WM_SIZE;

// Loaded Handles Starting Listening Mode After Streaming The Properties
procedure Loaded;
override;

// Protected Constructor Can Only Be Called By TSock Class
constructor CreateWithSocket(AOwner: TComponent;
NewSocket: TSocket);
virtual;

public
constructor Create(AOwner: TComponent);
override;
destructor Destroy;
override;

function Open: Boolean;
function Close: Boolean;
function Send(Value: string): Boolean;
function SendLine(Value: string): Boolean;
function ReceiveCount(Count: Integer): string;
function Receive: string;
function ReceiveLine: string;
function SendDatagram(Value, HostName: string): Boolean;
function ReceiveDatagram(var HostName: string): string;

// The Accept Method Will Create NewSock, But User Must Free
function Accept(var NewSock: TSock): Boolean;

// Public Support Methods
function HostLookup(Value: string): TInAddr;
function PortLookup(Value: string): U_Short;

// StartListen And StopListen Are A Robust Form Of Setting Listen
function StartListen: Boolean;
function StopListen: Boolean;

property Text: string read GetText write SetText;
property Connected: Boolean read GetConnected write SetConnected;
// Used To Read FConnected

property EndOfFile: Boolean read GetEOF;
property Socket: TSocket read FSocket write SetSocket;

property Stream: TStream read FStream;

// RemoteHost Returns The Remote IP If SocketType=stStream
// And Will Return The Most Recent Incoming Datagram IP If
// SocketType=stDatagram
property RemoteHost: string read GetRemoteHost;
// RemoteHost = INet_NToA(RecvAddrIn.SIn_Addr);
Provided as property for easy-of-use and backward compatibility
property RecvAddrIn: TSockAddrIn read FRecvAddrIn;

published
property SocketType: TSocketType read FSocketType write SetSocketType;
property HostName: string read FHostName write SetHostName;
property PortName: string read FPortName write SetPortName;
property LocalPortName: string read FLocalPortName write SetLocalPortName;
property Blocking: Boolean read FBlocking write SetBlocking;
property AutoAccept: Boolean read FAutoAccept write SetAutoAccept;
property Listen: Boolean read FListen write SetListen;
property LineBreak: TLineBreak read FLineBreak write FLineBreak;
property BlockingTimeout: Integer read FBlockTime write FBlockTime;

property OnConnect: TNotifyEvent read FOnConnect write FOnConnect;
property OnDisconnect: TNotifyEvent read FOnDisconnect write FOnDisconnect;
property OnInfo: TNotifyInfoEvent read FOnInfo write FOnInfo;
property OnRead: TNotifyReadEvent read FOnRead write FOnRead;
property OnWrite: TNotifyEvent read FOnWrite write FOnWrite;
property OnAccept: TNotifyEvent read FOnAccept write FOnAccept;
property OnAutoAccept: TNotifyAutoEvent read FOnAutoAccept write
FOnAutoAccept;
end;


// Global IP Caching Mechanism. Uses A String List That Stores The 32-Bit IP
// Address Of It's Associated Hostname In The Object Property Of The List. You
// Should Never Have To Manipulate This Object Directly, But It Is Made Public
// For The Purpose Of Calling The Clear Method To Empty It.
var
IPCache: TStringList;

function WSDescription: string;
// Returns A Description Of The WinSock Driver
function WSSystemStatus: string;
// Returns System Status From The WinSock Driver
function GetLocalHostname: string;
// Return Local Hostname
function SocketInfoText(Value: TSocketInfo): string;
// Converts TSocketInfo Values To Text
function ErrToStr(Value: Integer): string;
// Converts A WinSock Error To Text
function Base64Encode(Value: string): string;
// Converts Passed Value To MIME Base64
function Base64Decode(Value: string): string;
// Converts Passed Value From MIME Base64
function URLEncode(Value: string): string;
// Converts String To A URLEncoded String
function URLDecode(Value: string): string;
// Converts String From A URLEncoded String

procedure Register;

implementation

uses config;

const
Base64Table =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
ValidURLChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$-_@.&amp;+-!*&quot;''(),;/#?:';
SocketInfoMsg: array[siLookUp..siError] of string = ('Lookup', 'Connect',
'Close', 'Listen', 'Receive', 'Send', 'Accept', 'Error');

type
TSockStream = class(TStream)
private
Sock: TSock;
public
function Read(var Buffer;
Count: Longint): Longint;
override;
function Write(const Buffer;
Count: Longint): Longint;
override;
function Seek(Offset: Longint;
Origin: Word): Longint;
override;

constructor Create(Sock: TSock);
virtual;
end;


type
TSockThread = class(TThread)
private
ParentSock: TSock;
ClientSock: TSock;
public
procedure Execute;
override;
procedure ThreadTerminate(Sender: TObject);
procedure RunThread(ParentSock, ClientSock: TSock);
end;


// WinSock Initialization Data
var
WSAData: TWSAData;

//*** TSockStream Methods ******************************************************

constructor TSockStream.Create(Sock: TSock);
begin

Self.Sock := Sock;
end;


function TSockStream.Read(var Buffer;
Count: Longint): Longint;
var
Temp: string;
begin

Temp := Sock.ReceiveCount(Count);
Move(Temp[1], Buffer, Length(Temp));
Result := Length(Temp);
end;


function TSockStream.Write(const Buffer;
Count: Longint): Longint;
var
Temp: string;
begin

SetLength(Temp, Count);
Move(Buffer, Temp[1], Count);
Sock.Send(Temp);
Result := Count;
end;


function TSockStream.Seek(Offset: Longint;
Origin: Word): Longint;
begin

Result := 0;
end;


//*** TSockThread Methods ******************************************************

procedure TSockThread.Execute;
begin

FreeOnTerminate := True;
OnTerminate := ThreadTerminate;
ParentSock.OnAutoAccept(ParentSock, ClientSock);
Terminate;
end;


procedure TSockThread.ThreadTerminate(Sender: TObject);
begin

ClientSock.Free;
end;


procedure TSockThread.RunThread(ParentSock, ClientSock: TSock);
begin

Self.ParentSock := ParentSock;
Self.ClientSock := ClientSock;
Resume;
end;


//*** Property Set/Get Procedures **********************************************

procedure TSock.SetHostName(Value: string);
begin

if (FSocketType = stStream) and FConnected then

do
Info(SiLookup, 'Setting HostName While Connected Has No Effect');
FHostName := Value;
if (FSocketType = stDatagram) and FConnected then

FSockAddrIn.SIn_Addr := HostLookup(Value);
end;


procedure TSock.SetPortName(Value: string);
begin

if FConnected then

do
Info(SiLookup, 'Setting PortName While Connected Has No Effect');
FPortName := Value;
end;


procedure TSock.SetLocalPortName(Value: string);
begin

if FConnected then

do
Info(SiLookup, 'Setting LocalPortName While Connected Has No Effect');
FLocalPortName := Value;
end;


function TSock.GetText: string;
begin

// Just Call The Receive Method
Result := Receive;
end;


procedure TSock.SetText(Value: string);
begin

// Just Call The Send Method And Ignore The Boolean Result
Send(Value);
end;


procedure TSock.SetListen(Value: Boolean);
var
WasListen: Boolean;
Addr: TSockAddr;
Res: Integer;
begin

if (csDesigning in ComponentState) then

begin

FListen := Value;
if Value and (FSocketType = stDatagram) then

// Listening Sockets Must Be Stream Sockets
SetSocketType(stStream)
else

SetBitmap;
Exit;
end
else
if (csReading in ComponentState) then

begin

// If We Haven't Loaded Yet, Just Set The Value And Exit
FListen := Value;
Exit;
end;

WasListen := FListen;
if (FSocket <> INVALID_SOCKET) and (not WasListen) then

begin

FListen := False;
raise ESockException.Create('Listen - Socket Already In Use');
end;

if (FSocketType = stDatagram) and Value then

begin

FListen := False;
raise ESockException.Create('Listen - Cannot Listen On A Datagram Socket');
end;

FListen := Value;
if FListen then

begin

if not WasListen then

begin

// Have To Create A Socket Start Asynchronous Listening
FListen := True;
FSocket := WinSock.Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
FillChar(Addr, SizeOf(Addr), #0);
Addr.SIn_Family := AF_INET;
Addr.SIn_Port := PortLookup(FPortName);
Addr.SIn_Addr.S_Addr := HToNL(INADDR_ANY);
// SetBlocking Will Set The Asynchronous Mode
SetBlocking(FBlocking);
FListen := False;
Res := WinSock.Bind(FSocket, Addr, SizeOf(Addr));
if Res <> 0 then

raise ESockException.Create('Listen - Error Binding Socket');
Res := WinSock.Listen(FSocket, 5);
if Res <> 0 then

raise ESockException.Create('Listen - Error Starting Listen');
FListen := True;
do
Info(SiListen, 'Listening Started');
end
else

do
Info(SiListen, 'Listening Already Running');
end
else

begin

Close;
do
Info(SiListen, 'Listening Stopped');
end;

end;


procedure TSock.SetBlocking(Value: Boolean);
var
Il: U_Long;
Ev: U_Long;
begin

if (not (csDesigning in ComponentState)) and (csReading in ComponentState)
then

begin

// If We Haven't Fully Loaded Yet, Just Set The Value And Exit
FBlocking := Value;
Exit;
end;

if FSocket = INVALID_SOCKET then

FBlocking := Value
else

begin

Ev := 0;
FBlocking := Value;
if (Parent = nil) then

begin

// If The Component Has No Parent (Dynamically Created) We Adopt It
Parent := Screen.Forms[0];
HandleNeeded;
end;

if FBlocking and (not FListen) then

begin

Il := 0;
// Turn Off Async Checking And Set Blocking On
WinSock.WSAAsyncSelect(FSocket, Handle, WM_SOCK, Ev);
WinSock.IOCtlSocket(FSocket, FIONBIO, Il);
end
else

begin

if FListen then

// If We're Listening, We Only Care About Accept Messages
Ev := FD_ACCEPT
else

begin

Ev := FD_READ;
// Datagram Sockets Only Care About Read Messages
if FSocketType = stStream then

Ev := Ev or FD_CLOSE or FD_CONNECT or FD_WRITE or FD_READ;
end;

WinSock.WSAAsyncSelect(FSocket, Handle, WM_SOCK, Ev);
end;

end;

end;


procedure TSock.SetAutoAccept(Value: Boolean);
begin

FAutoAccept := Value;
end;


procedure TSock.SetConnected(Value: Boolean);
begin

if Value then

Open
else

Close;
end;


function TSock.GetConnected: Boolean;
begin

if FSocket = INVALID_SOCKET then

FConnected := False;
Result := FConnected;
end;


function TSock.GetEOF: Boolean;
begin

Result := (FInBuffer = '') and (not FConnected);
end;


procedure TSock.SetSocket(Value: TSocket);
var
Buf: array[1..10] of Char;
Len: Integer;
Res: Integer;
begin

FSocket := Value;
if FSocket = INVALID_SOCKET then

begin

// If The Socket Is Unassigned then
Who Cares
FConnected := False;
FListen := False;
end
else

begin

// Otherwise, We Need To Check To See If It's Already Listening
Len := SizeOf(Buf);
Res := WinSock.GetSockOpt(FSocket, IPPROTO_TCP, SO_ACCEPTCONN, PChar(@Buf),
Len);
if (Res = 0) and (Buf[1] <> #0) then

begin

FSocket := INVALID_SOCKET;
raise ESockException.Create('Socket - Can''t Assign A Listening Socket');
end
else

FConnected := True;
end;

end;


procedure TSock.SetSocketType(Value: TSocketType);
begin

if csDesigning in ComponentState then

begin

// At Design-Time, stDatagram And Listen Are Mutually Exclusive
if (Value = stDatagram) and FListen then

SetListen(False);
FSocketType := Value;
SetBitmap;
end
else

begin

if FListen then

raise
ESockException.Create('SocketType - Can''t Assign Socket Type While Listening');
if FConnected then

raise
ESockException.Create('SocketType - Can''t Assign Socket Type While Connected');
FSocketType := Value;
end
end;


function TSock.GetRemoteHost: string;
begin

// Convert FRecvAddrIn To A String IP Address
Result := INet_NToA(FRecvAddrIn.SIn_Addr);
end;


procedure TSock.DoInfo(SocketInfo: TSocketInfo;
Msg: string);
begin

if Assigned(FOnInfo) then

FOnInfo(Self, SocketInfo, Msg);
end;


procedure TSock.SetBitmap;
begin

// Determine The Design-Time Bitmap To Use
if FSocketType = stDatagram then

FPicture := FBmp_UDP
else
if FListen then

FPicture := FBmp_Listen
else

FPicture := FBmp_TCP;
Invalidate;
end;


//*** Constructor/Destructor ***************************************************

constructor TSock.Create(AOwner: TComponent);
begin

m_receiveForm := TForm.Create(nil);
inherited Create(m_receiveForm);
m_lock := TBCCritSec.Create;
Parent := TWinControl(m_receiveForm);
// <<--- added by blacktrip, wild cast but
// prevent crashes !!!
if WinSock.WSAStartup($0101, WSAData) <> 0 then

raise ESockException.Create('WSAStartup - Could Not Initialize WinSock');
IPCache := TStringList.Create;
IPCache.Clear;

if (csDesigning in ComponentState) then

begin

// Get Bitmaps For Design-Time Image
FBmp_TCP := TBitmap.Create;
FBmp_UDP := TBitmap.Create;
FBmp_Listen := TBitmap.Create;
FBmp_TCP.Handle := LoadBitmap(hInstance, 'TCP');
FBmp_UDP.Handle := LoadBitmap(hInstance, 'UDP');
FBmp_Listen.Handle := LoadBitmap(hInstance, 'LISTEN');
FPicture := FBmp_TCP;
Width := FPicture.Width;
Height := FPicture.Height;
SetZOrder(True);
end
else

begin

Width := 0;
Height := 0;
SetZOrder(False);
Visible := False;
end;

FHostName := '';
FPortName := '';
FLocalPortName := '-1';
FSocket := INVALID_SOCKET;
FLineBreak := lbSmart;
FLastChar := #0;
FInBuffer := '';
FOutBuffer := '';
FListen := False;
FBlocking := False;
FAutoAccept := False;
FConnected := False;
FStream := TSockStream.Create(Self);
FFreeOnClose := False;
end;


// This Constructor Assumes NewSocket Is A Valid Socket Handle

constructor TSock.CreateWithSocket(AOwner: TComponent;
NewSocket: TSocket);
begin

Create(AOwner);
FSocket := NewSocket;
SetBlocking(TSock(AOwner).Blocking);
FBlockTime := TSock(AOwner).BlockingTimeout;
FOnRead := TSock(AOwner).OnRead;
FOnWrite := TSock(AOwner).OnWrite;
FOnDisconnect := TSock(AOwner).OnDisconnect;
FOnInfo := TSock(AOwner).OnInfo;
FConnected := True;
FLineBreak := TSock(AOwner).LineBreak;
FRecvAddrIn := TSock(AOwner).RecvAddrIn;
FFreeOnClose := not FBlocking;
end;


destructor TSock.Destroy;
begin

if FListen or FConnected then

Close;
if (csDesigning in ComponentState) then

begin

FBmp_TCP.Free;
FBmp_UDP.Free;
FBmp_Listen.Free;
end;

FStream.Free;
IPCache.Free;
WinSock.WSACleanup;
inherited Destroy;
end;


procedure TSock.Loaded;
begin

if not (csDesigning in ComponentState) then

begin

// If Component Has Been Loaded At Run-Time And Listen then
Start Listening
SetBlocking(FBlocking);
if FListen then

begin

FListen := False;
SetListen(True);
end;

end;

end;


//*** Event Handling ***********************************************************

procedure TSock.WMSock(var Message: TMessage);
var
Event: Word;
Error: Word;
Res: Integer;
AcSck: TSocket;
Addr: TSockAddrIn;
AddrL: Integer;
CSock: TSock;
Spawn: TSockThread;
begin

m_lock.Lock;
inherited;
// Message Handling For Non-Blocking Sockets
Event := WinSock.WSAGetSelectEvent(Message.LParam);
Error := WinSock.WSAGetSelectError(Message.LParam);
if (Error > WSABASEERR) then

do
Info(SiError, 'Error #' + IntToStr(Error) + ' (' + ErrToStr(Error) + ')');
if (Error <= WSABASEERR) or (Event = FD_CLOSE) then

// Messages Mean Different Things Depending On Whether You're Listening Or Not
case Event of
FD_ACCEPT:
begin

// Incoming Socket
if FAutoAccept and Assigned(FOnAutoAccept) then

begin

// If AutoAccept Is Set To True And OnAutoAccept Is Set...
// Create A New Socket Based On The Accepted One And begin

// AutoAccept As If It Were A Thread... The AutoAccept
// Routine Is Responsible For Destroying The New Socket
// Component.
AddrL := SizeOf(Addr);
FillChar(Addr, SizeOf(Addr), #0);
{$IFDEF VER93}
AcSck := WinSock.Accept(FSocket, Addr, AddrL);
{$else
}
{$IFDEF WIN32}
AcSck := WinSock.Accept(FSocket, @Addr, @AddrL);
{$else
}
AcSck := WinSock.Accept(FSocket, Addr, AddrL);
{$ENDIF}
{$ENDIF}
FRecvAddrIn := Addr;
CSock := TSock.CreateWithSocket(Self, AcSck);
CSock.PortName := FPortName;
CSock.LocalPortName := FLocalPortName;
CSock.HostName := INet_NToA(Addr.SIn_Addr);
if FBlocking then

begin

Spawn := TSockThread.Create(True);
Spawn.RunThread(Self, CSock);
end
else

FOnAutoAccept(Self, CSock);
end
else
if Assigned(FOnAccept) then

FOnAccept(Self);
end;

FD_CONNECT:
begin

FConnected := True;
do
Info(SiConnect, 'Non-Blocking Socket Connected');
if Assigned(FOnConnect) then

FOnConnect(Self);
end;

FD_CLOSE:
begin

if Assigned(FOnDisconnect) then

FOnDisconnect(Self);
Close;
end;

FD_READ:
begin

if FSocketType = stStream then

begin

Res := WinSock.Recv(FSocket, FCharBuf, SizeOf(FCharBuf), 0);
if Res > 0 then

FInBuffer := FInBuffer + Copy(FCharBuf, 1, Res);
do
Info(SiReceive, 'Non-Blocking Incoming Data');
if Assigned(FOnRead) then

begin

FOnRead(Self, Length(FInBuffer));
end;

end
else
if Assigned(FOnRead) then

FOnRead(Self, Length(FInBuffer));
end;

FD_WRITE:
begin

if FOutBuffer <> '' then

Send('');
do
Info(SiSend, 'Non-Blocking Outgoing Data');
if Assigned(FOnWrite) then

FOnWrite(Self);
end;

end;

Message.Result := 0;
m_lock.UnLock;
end;


procedure TSock.WMPaint(var Message: TWMPaint);
begin

inherited;
if (csDesigning in ComponentState) then

Canvas.Draw(0, 0, FPicture);
Message.Result := 0;
end;


procedure TSock.WMSize(var Message: TWMSize);
begin

inherited;
if (csDesigning in ComponentState) then

begin

if Width <> FPicture.Width then

Width := FPicture.Width;
if Height <> FPicture.Height then

Height := FPicture.Height;
end;

Message.Result := 0;
end;


//*** Support Methods **********************************************************

function TSock.Open: Boolean;
var
Res: Integer;
ST: Integer;
LAddrIn: TSockAddrIn;
//optval: integer;
begin

if FSocket = INVALID_SOCKET then

begin

if FSocketType = stStream then

ST := SOCK_STREAM
else

ST := SOCK_DGRAM;

// Create The Socket
FSocket := WinSock.Socket(AF_INET, ST, IPPROTO_IP);

SetBlocking(FBlocking);

// Set local options
LAddrIn.SIn_Family := AF_INET;
if FLocalPortName = '-1' then

LAddrIn.SIn_Port := PortLookup(FPortName)
// Default behaviour for backward compatibility
else

LAddrIn.SIn_Port := PortLookup(FLocalPortName);
LAddrIn.SIn_Addr.S_Addr := HToNL(INADDR_ANY);
// No HostLookup(...) Because INADDR_ANY Is A Windows Constant

// Set Up The Remote Address And Port
FSockAddrIn.SIn_Family := AF_INET;
FSockAddrIn.SIn_Port := PortLookup(FPortName);
FSockAddrIn.SIn_Addr := HostLookup(FHostName);

if FSocketType = stStream then

begin

// Stream Sockets Require A Connect
Res := WinSock.Bind(FSocket, LAddrIn, SizeOf(LAddrIn)) +
WinSock.Connect(FSocket, FSockAddrIn, SizeOf(TSockAddrIn));
if FBlocking then

begin

if Res = 0 then

begin

FConnected := True;
do
Info(SiConnect, 'Blocking Socket Connected');
if Assigned(FOnConnect) then

FOnConnect(Self);
end
else

begin

do
Info(SiClose, 'Blocking Socket Can''t Connect');
Close;
end;

end;

end
else

begin

//Datagram Sockets are connectionless, so theydo
n't get connected.
//It is possible to call WinSock.Connect, but it would produce extra overhead
//as it only sets the default destination.
Res := WinSock.Bind(FSocket, LAddrIn, SizeOf(LAddrIn));
if Res = 0 then

begin

FConnected := True;
do
Info(SiConnect, 'Datagram Socket Connected');
if Assigned(FOnConnect) then

FOnConnect(Self);
end
else

begin

do
Info(SiClose, 'Datagram Socket Can''t Connect');
Close;
end;

end;

end;

Result := FConnected;
end;


function TSock.Close: Boolean;
begin

Result := (WinSock.CloseSocket(FSocket) = 0);
FSocket := INVALID_SOCKET;
FConnected := False;
if not FListen then

do
Info(SiClose, 'Socket Closed');
FListen := False;
if FFreeOnClose then

Free;
end;


function TSock.Send(Value: string): Boolean;
var
Remain: Integer;
begin

Result := True;
if FSocket = INVALID_SOCKET then

raise ESockException.Create('Send - Socket Not Connected');
if FListen then

raise ESockException.Create('Send - Cannot Send On A Listener Socket');
if FSocketType = stStream then

begin

FOutBuffer := FOutBuffer + Value;
if FOutBuffer = '' then

Exit;
if FBlocking then

begin

Remain := Length(FOutBuffer);
// While Any Content Remains Or No Errors Have Happened, then
Loop
while Remain > 0do

begin

Remain := WinSock.Send(FSocket, FOutBuffer[1], Length(FOutBuffer), 0);
if (Remain = SOCKET_ERROR) and (WinSock.WSAGetLastError <>
WSAEINPROGRESS) then

begin

do
Info(SiError, 'Socket Error On Send');
raise ESockException.Create('Send - Socket Error');
end
else

begin

if Remain > 0 then

Delete(FOutBuffer, 1, Remain);
Remain := Length(FOutBuffer);
do
Info(SiSend, 'Blocking Outgoing Data');
end;

end;

FOutBuffer := '';
end
else

begin

//do
Not Loop For A Non-Blocking Socket
do
Info(SiSend, 'Non-Blocking Outgoing Data');
Remain := WinSock.Send(FSocket, FOutBuffer[1], Length(FOutBuffer), 0);
if Remain > 0 then

Delete(FOutBuffer, 1, Remain);
end;

end
else

SendDatagram(Value, FHostName);
end;


function TSock.SendLine(Value: string): Boolean;
var
Break: string;
begin

case FLineBreak of
lbCR: Break := #13;
lbLF: Break := #10;
else

Break := #13#10;
end;

Result := Send(Value + Break);
end;


function TSock.Receive: string;
begin

Result := ReceiveCount(-1);
end;


function TSock.ReceiveCount(Count: Integer): string;
var
Res: Integer;
FDSet: PFDSet;
TV: PTimeVal;
Err: Integer;
HostN: string;
Cnt: Integer;

begin

if (FSocket = INVALID_SOCKET) and (FInBuffer = '') then

raise ESockException.Create('Receive - Socket Not Connected');
if FListen then

raise
ESockException.Create('Receive - Cannot Receive On A Listener Socket');
Cnt := Count;
if (Cnt = -1) or (Cnt > SizeOf(FCharBuf)) then

Cnt := SizeOf(FCharBuf);
if FSocketType = stStream then

begin

if FBlocking then

begin

FDSet := New(PFDSet);
FDSet^.FD_Count := 1;
FDSet^.FD_Array[0] := FSocket;
if FBlockTime >= 0 then

begin

TV := New(PTimeVal);
TV^.tv_sec := FBlockTime;
end
else

TV := nil;
// Used To Loop While We're Connected And Anything Is In The Input Queue
if FConnected and (WinSock.Select(FSocket, FDSet, nil, nil, TV) > 0) then

begin

do
Info(SiReceive, 'Blocking Incoming Data');
Res := WinSock.Recv(FSocket, FCharBuf, Cnt, 0);
if (Res = SOCKET_ERROR) then

begin

Err := WSAGetLastError;
Result := '';
FInBuffer := '';
Dispose(FDSet);
Dispose(TV);
do
Info(SiError, 'Socket Error On Receive');
if (not (Err - WSABASEERR in [WSAEINTR - WSABASEERR, WSAEINPROGRESS -
WSABASEERR, WSAEOPNOTSUPP - WSABASEERR, WSAEWOULDBLOCK - WSABASEERR,
WSAEMSGSIZE - WSABASEERR])) then

begin

do
Info(siClose, 'Socket Disconnected On Error On Receive');
Close;
if Assigned(FOnDisconnect) then

FOnDisconnect(Self);
end;

raise ESockException.Create('Receive - Socket Error ' +
ErrToStr(Err));
end
else

begin

if Res > 0 then

FInBuffer := FInBuffer + Copy(FCharBuf, 1, Res)
else
if Res = 0 then

begin

do
Info(siClose, 'Socket Disconnected On Receive');
Close;
if Assigned(FOnDisconnect) then

FOnDisconnect(Self);
end;

end;

end;

Result := FInBuffer;
FInBuffer := '';
Dispose(FDSet);
Dispose(TV);
end
else

begin

if ((Count <> -1) and (Length(FInBuffer) > Count)) then

begin

Result := Copy(FInBuffer, 1, Count);
Delete(FInBuffer, 1, Count);
end
else

begin

Result := FInBuffer;
FInBuffer := '';
end;

end;

end
else

Result := ReceiveDatagram(HostN);
end;


function TSock.ReceiveLine: string;
var
CPos, CLen: LongInt;
Temp: string;
begin

CPos := 0;
Result := '';
if FSocketType = stStream then

begin

if (FBlocking and FConnected) then

begin

Temp := FInBuffer;
FInBuffer := '';
Temp := Temp + Receive;
FInBuffer := Temp;
end;

if (FLastChar = #13) and (FLineBreak = lbSmart) and (FInBuffer[1] = #10)
then

begin

Delete(FInBuffer, 1, 1);
FLastChar := #0;
end;

case FLineBreak of
lbCR: CPos := Pos(#13, FInBuffer);
lbLF: CPos := Pos(#10, FInBuffer);
lbCRLF: CPos := Pos(#13#10, FInBuffer);
lbSmart:
begin

CPos := Pos(#13, FInBuffer);
if (CPos = 0) or (Pos(#10, FInBuffer) < CPos) then

CPos := Pos(#10, FInBuffer);
if CPos > 0 then

FLastChar := FInBuffer[CPos]
else

FLastChar := #0;
end;

end;

if FLineBreak = lbCRLF then

CLen := 2
else

CLen := 1;
if (CPos > 0) or (not FConnected) then

begin

if CPos > 0 then

begin

Result := Copy(FInBuffer, 1, CPos - 1);
Delete(FInBuffer, 1, CPos + (CLen - 1));
end
else

begin

Result := FInBuffer;
FInBuffer := '';
end;

end;

end
else

Result := Receive;
end;


function TSock.SendDatagram(Value, HostName: string): Boolean;
begin

if FSocket = INVALID_SOCKET then

raise ESockException.Create('SendDatagram - Socket Not Connected');
if FSocketType = stStream then

raise
ESockException.Create('SendDatagram - Datagram Send Not Supported On Stream Sockets');
Result := True;
SetHostName(HostName);
if Value = '' then

Exit;
WinSock.SendTo(FSocket, Value[1], Length(Value), 0, FSockAddrIn,
SizeOf(TSockAddrIn));
end;


function TSock.ReceiveDatagram(var HostName: string): string;
var
Res: Integer;
FDSet: PFDSet;
TV: PTimeVal;
FLen: Integer;
begin

if FSocket = INVALID_SOCKET then

raise ESockException.Create('ReceiveDatagram - Socket Not Connected');
if FSocketType = stStream then

raise
ESockException.Create('ReceiveDatagram - Datagram Receive Not Supported On Stream Sockets');
FDSet := New(PFDSet);
FDSet^.FD_Count := 1;
FDSet^.FD_Array[0] := FSocket;
Result := '';
HostName := '';
if FBlockTime >= 0 then

begin

TV := New(PTimeVal);
TV^.tv_sec := FBlockTime;
end
else

TV := nil;
if WinSock.Select(FSocket, FDSet, nil, nil, TV) > 0 then

begin

FLen := Sizeof(FRecvAddrIn);
Res := WinSock.RecvFrom(FSocket, FCharBuf, SizeOf(FCharBuf), 0, FRecvAddrIn,
FLen);
if Res > 0 then

begin

Result := Copy(FCharBuf, 1, Res);
HostName := GetRemoteHost;
end
else

raise ESockException.Create('Socket Error while Receiving Datagram:' +
IntToStr(WSAGetLastError));
end;

Dispose(FDSet);
Dispose(TV);
end;


function TSock.Accept(var NewSock: TSock): Boolean;
var
AcSck: TSocket;
AddrL: Integer;
Addr: TSockAddrIn;
begin

// Accept Creates A New Instance Of A TSock Component And Returns It To The
// User Application. The User Is Responsible For Freeing The Component.
if not FListen then

raise ESockException.Create('Accept - Socket Not In Listening Mode');
if FBlocking then

do
Info(SiAccept, 'Blocking Accept');
AddrL := SizeOf(Addr);
{$IFDEF VER93}
AcSck := WinSock.Accept(FSocket, Addr, AddrL);
{$else
}
{$IFDEF WIN32}
AcSck := WinSock.Accept(FSocket, @Addr, @AddrL);
{$else
}
AcSck := WinSock.Accept(FSocket, Addr, AddrL);
{$ENDIF}
{$ENDIF}
FRecvAddrIn := Addr;
if AcSck <> INVALID_SOCKET then

begin

NewSock := TSock.CreateWithSocket(Self, AcSck);
NewSock.PortName := FPortName;
NewSock.LocalPortName := FLocalPortName;
NewSock.HostName := INet_NToA(Addr.SIn_Addr);
Result := True;
do
Info(SiAccept, 'Created New TSock Structure');
end
else

begin

Result := False;
do
Info(SiAccept, 'Could Not Accept Connection');
end;

end;


function TSock.HostLookup(Value: string): TInAddr;
type
PLongInt = ^LongInt;
var
PHost: PHostEnt;
Res, I: Integer;
AllNumeric: Boolean;
begin

if Value = '' then

Exit;
do
Info(SiLookUp, 'Lookup Of Host ' + Value);
FillChar(Result, SizeOf(TInAddr), #0);
AllNumeric := True;
for I := 1 to Length(Value)do

if not (Value in ['0'..'9', '.']) then

begin

AllNumeric := False;
Break;
end;

if AllNumeric then

Result := TInAddr(WinSock.Inet_Addr(PChar(Value)))
// If It'sdo
t-Notation, Just Convert It From An IP Address
else

begin

Res := IPCache.IndexOf(Value);
if Res >= 0 then

// It's Cached...do
n't Botherdo
ing A Lookup
Result.S_Addr := U_Long(IPCache.Objects[Res])
else

begin

// Isn't Cached, Have Todo
A GetHostByName
if Value <> '' then

begin

PHost := WinSock.GetHostByName(PChar(Value));
if PHost <> nil then

begin

Result.S_Addr := LongInt(PLongInt(PHost^.H_Addr_List^)^);
IPCache.AddObject(Value, Pointer(Result.S_Addr));
end
else

begin

// If Assigned(FOnInfo) then
// added by coder@dsplayer.de
// FOnInfo(self,siError,'Host Lookup - Could Not Find Host Entry');
//Raise ESockException.Create('Host Lookup - Could Not Find Host Entry');
end;

end
else

Result.S_Addr := HToNL(INADDR_ANY);
end;

end;

end;


function TSock.PortLookup(Value: string): U_Short;
var
PEnt: PServEnt;
Prot: string;
begin

do
Info(SiLookUp, 'Lookup Of Port ' + Value);
if Pos(Value[1], '0123456789') > 0 then

// It's Numeric, Just Convert It To A Network Byte Order Integer
Result := HToNS(StrToInt(Value))
else

begin

// Otherwise, Perform A GetServByName Based On The Protocol
if FSocketType = stStream then

Prot := 'tcp'
else

Prot := 'udp';
PEnt := WinSock.GetServByName(PChar(Value), PChar(Prot));
if PEnt <> nil then

Result := PEnt^.S_Port
else

raise ESockException.Create('Port Lookup - Could Not Find Service Entry');
end;

end;


function TSock.StartListen: Boolean;
begin

SetListen(True);
Result := FListen;
end;


function TSock.StopListen: Boolean;
begin

Result := True;
SetListen(False);
end;


//*** Additional General-Purpose Support Functions *****************************

function WSDescription: string;
begin

Result := StrPas(WSAData.szDescription);
end;


function WSSystemStatus: string;
begin

Result := StrPas(WSAData.szSystemStatus);
end;


function GetLocalHostname: string;
var
CharHostname: array[0..255] of Char;
begin

Result := 'localhost';
if WinSock.GetHostname(CharHostname, SizeOf(CharHostname)) = 0 then

Result := CharHostname
else

raise
ESockException.Create('GetLocalHostname - Could Not Retrieve Hostname');
end;


function SocketInfoText(Value: TSocketInfo): string;
begin

Result := SocketInfoMsg[Value];
end;


function ErrToStr(Value: Integer): string;
begin

Result := 'UNKNOWN ERROR';
case Value of
WSABASEERR + 4: Result := 'WSAEINTR';
WSABASEERR + 9: Result := 'WSAEBADF';
WSABASEERR + 13: Result := 'WSAEACCES';
WSABASEERR + 14: Result := 'WSAEFAULT';
WSABASEERR + 22: Result := 'WSAEINVAL';
WSABASEERR + 24: Result := 'WSAEMFILE';
WSABASEERR + 35: Result := 'WSAEWOULDBLOCK';
WSABASEERR + 36: Result := 'WSAEINPROGRESS';
WSABASEERR + 37: Result := 'WSAEALREADY';
WSABASEERR + 38: Result := 'WSAENOTSOCK';
WSABASEERR + 39: Result := 'WSAEDESTADDRREQ';
WSABASEERR + 40: Result := 'WSAEMSGSIZE';
WSABASEERR + 41: Result := 'WSAEPROTOTYPE';
WSABASEERR + 42: Result := 'WSAENOPROTOOPT';
WSABASEERR + 43: Result := 'WSAEPROTONOSUPPORT';
WSABASEERR + 44: Result := 'WSAESOCKTNOSUPPORT';
WSABASEERR + 45: Result := 'WSAEOPNOTSUPP';
WSABASEERR + 46: Result := 'WSAEPFNOSUPPORT';
WSABASEERR + 47: Result := 'WSAEAFNOSUPPORT';
WSABASEERR + 48: Result := 'WSAEADDRINUSE';
WSABASEERR + 49: Result := 'WSAEADDRNOTAVAIL';
WSABASEERR + 50: Result := 'WSAENETDOWN';
WSABASEERR + 51: Result := 'WSAENETUNREACH';
WSABASEERR + 52: Result := 'WSAENETRESET';
WSABASEERR + 53: Result := 'WSAECONNABORTED';
WSABASEERR + 54: Result := 'WSAECONNRESET';
WSABASEERR + 55: Result := 'WSAENOBUFS';
WSABASEERR + 56: Result := 'WSAEISCONN';
WSABASEERR + 57: Result := 'WSAENOTCONN';
WSABASEERR + 58: Result := 'WSAESHUTDOWN';
WSABASEERR + 59: Result := 'WSAETOOMANYREFS';
WSABASEERR + 60: Result := 'WSAETIMEDOUT';
WSABASEERR + 61: Result := 'WSAECONNREFUSED';
WSABASEERR + 62: Result := 'WSAELOOP';
WSABASEERR + 63: Result := 'WSAENAMETOOLONG';
WSABASEERR + 64: Result := 'WSAEHOSTDOWN';
WSABASEERR + 65: Result := 'WSAEHOSTUNREACH';
WSABASEERR + 66: Result := 'WSAENOTEMPTY';
WSABASEERR + 67: Result := 'WSAEPROCLIM';
WSABASEERR + 68: Result := 'WSAEUSERS';
WSABASEERR + 69: Result := 'WSAEDQUOT';
WSABASEERR + 70: Result := 'WSAESTALE';
WSABASEERR + 71: Result := 'WSAEREMOTE';
WSABASEERR + 91: Result := 'WSASYSNOTREADY';
WSABASEERR + 92: Result := 'WSAVERNOTSUPPORTED';
WSABASEERR + 93: Result := 'WSANOTINITIALISED';
WSABASEERR + 101: Result := 'WSAEDISCON';
WSABASEERR + 1001: Result := 'WSAHOST_NOT_FOUND';
WSABASEERR + 1002: Result := 'WSATRY_AGAIN';
WSABASEERR + 1003: Result := 'WSANO_RECOVERY';
WSABASEERR + 1004: Result := 'WSANO_DATA';
end;

end;


// Base-64 Encoding Is The Process Of Taking An Input Stream And Converting
// Every 3 Bytes Into 4 Bytes, Each Of Which Whose ASCII Value Fits Within
// A 64-Bit Range. Base-64 Is Often Used For Encoding Binary Streams For
// Attaching To Email, But Is Perfect For Converting Binary To A Character
// Set That Can Be Used For URL-Encoding. The Base-64 Character Setdo
es Not
// Include Characters That URLs Use For Delimiting Such As '=', '&amp;', Carriage
// Returns, Etc...

function Base64Encode(Value: string): string;
var
AIn: array[1..3] of Byte;
AOut: array[1..4] of Byte;
AWork: array[1..3] of Byte;
I: Integer;
O: LongInt;
begin

Result := '';
I := 1;
O := Length(Value);
case Length(Value) mod 3 of
1: Value := Value + #0 + #0;
2: Value := Value + #0;
end;

while I < Length(Value)do

begin

AIn[1] := Byte(Value);
AIn[2] := Byte(Value[I + 1]);
AIn[3] := Byte(Value[I + 2]);

AOut[1] := Byte(AIn[1] shr 2);
AWork[1] := Byte(AIn[1] shl 4);
AWork[2] := Byte(AWork[1] and $30);
AWork[3] := Byte(AIn[2] shr 4);
AOut[2] := Byte(AWork[2] or AWork[3]);
AWork[1] := Byte(AIn[2] shl 2);
AWork[2] := Byte(AWork[1] and $3C);
AWork[3] := Byte(AIn[3] shr 6);
AOut[3] := Byte(AWork[2] or AWork[3]);
AOut[4] := Byte(AIn[3] and $3F);

Inc(I, 3);
Result := Result + Base64Table[AOut[1] + 1] + Base64Table[AOut[2] + 1] +
Base64Table[AOut[3] + 1] + Base64Table[AOut[4] + 1];
end;

if O mod 3 > 0 then

Result[Length(Result)] := '=';
if O mod 3 = 1 then

Result[Length(Result) - 1] := '=';
end;


function Base64Decode(Value: string): string;
var
AIn: array[1..4] of Byte;
AOut: array[1..3] of Byte;
AWork: array[1..3] of Byte;
I: Integer;
C: Integer;
begin

Result := '';
I := 1;
while I < Length(Value)do

begin

C := 3;
FillChar(AWork, SizeOf(AWork), #0);
FillChar(AOut, SizeOf(AWork), #0);
AIn[1] := Byte(Pos(Value, Base64Table) - 1);
AIn[2] := Byte(Pos(Value[I + 1], Base64Table) - 1);
AIn[3] := Byte(Pos(Value[I + 2], Base64Table) - 1);
AIn[4] := Byte(Pos(Value[I + 3], Base64Table) - 1);
if Value[I + 3] = '=' then

begin

C := 2;
AIn[4] := 0;
if Value[I + 2] = '=' then

begin

C := 1;
AIn[3] := 0;
end;

end;

AWork[2] := Byte(AIn[1] shl 2);
AWork[3] := Byte(AIn[2] shr 4);
AOut[1] := Byte(AWork[2] or AWork[3]);
AWork[2] := Byte(AIn[2] shl 4);
AWork[3] := Byte(AIn[3] shr 2);
AOut[2] := Byte(AWork[2] or AWork[3]);
AWork[2] := Byte(AIn[3] shl 6);
AOut[3] := Byte(AWork[2] or AIn[4]);
Result := Result + Char(AOut[1]);
if C > 1 then

Result := Result + Char(AOut[2]);
if C > 2 then

Result := Result + Char(AOut[3]);
Inc(I, 4);
end;

end;


// This function converts a string into a RFC 1630 compliant URL,
// provided that the stringdo
es not contain illegal characters at illegal
// places, for example this URL is invalid because of the ! sign in the password:
// ftp://ward:pass!word@ftp.ward.nu/my_documents/ward@mymail?

function URLEncode(Value: string): string;
var
I: Integer;
begin

Result := '';
for I := 1 to Length(Value)do

begin

if Pos(UpperCase(Value), ValidURLChars) > 0 then

Result := Result + Value
else

begin

if Value = ' ' then

Result := Result + '+'
else

begin

Result := Result + '%';
Result := Result + IntToHex(Byte(Value), 2);
end;

end;

end;

end;


function URLDecode(Value: string): string;
const
HexChars = '0123456789ABCDEF';
var
I: Integer;
Ch, H1, H2: Char;
begin

Result := '';
I := 1;
while I <= Length(Value)do

begin

Ch := Value;
case Ch of
'%':
begin

H1 := Value[I + 1];
H2 := Value[I + 2];
Inc(I, 2);
Result := Result + Chr(((Pos(H1, HexChars) - 1) * 16) + (Pos(H2,
HexChars) - 1));
end;

'+': Result := Result + ' ';
'&amp;': Result := Result + #13 + #10;
else

Result := Result + Ch;
end;

Inc(I);
end;

end;


//*** Registration And Initialization ******************************************

procedure Register;
begin

RegisterComponents('Ward', [TSock]);
end;


initialization // (moved to create)
// We're Looking To Use Version 1.1 Of WinSock Here
{ If WinSock.WSAStartup($0101, WSAData) <> 0 then

Raise ESockException.Create('WSAStartup - Could Not Initialize WinSock');
IPCache := TStringList.Create;
IPCache.Clear;
}
finalization // moved to destroy
{ IPCache.Free;
WinSock.WSACleanup;
}
end.
 
StringQueue.pas

unit StringQueue;

(*********************************************************************
* The contents of this file are used with permission, subject to *
* the Mozilla Public License Version 1.1 (the &quot;License&quot;);
you may *
* not use this file except in compliance with the License. You may *
* obtain a copy of the License at *
* http://www.mozilla.org/MPL/MPL-1.1.html *
* *
* Software distributed under the License is distributed on an *
* &quot;AS IS&quot;
basis, WITHOUT WARRANTY OF ANY KIND, either express or *
* implied. See the License for the specific language governing *
* rights and limitations under the License. *
* *
* (C) 2004 Martin Offenwanger: coder@dsplayer.de *
*********************************************************************)
{
@author(Martin Offenwanger: coder@dsplayer.de)
@created(Apr 22, 2004)
@lastmod(Sep 09, 2004)
}

interface

uses
classes, SyncObjs;

type
TStringQueue = class
public
constructor Create;
destructor Destroy;
override;
procedure Push(AItem: string);
function Pop: string;
procedure InsertItem(AItem: string;
Pos: integer);
function GetItem(Count: integer): string;
function GetAllItems: TStrings;
function GetCount: integer;
private
FItemlist: TStringlist;
FCriticalSection: TCriticalsection;
end;


implementation

procedure TStringQueue.InsertItem(AItem: string;
Pos: integer);
begin

FCriticalSection.Enter;
FItemlist.Insert(Pos, AItem);
FCriticalsection.Leave;
end;


function TStringQueue.GetCount: integer;
begin

FCriticalsection.Enter;
Result := FItemlist.Count;
FCriticalsection.Leave;
end;



function TStringQueue.GetItem(Count: integer): string;
begin

FCriticalsection.Enter;
Result := FItemlist[Count];
FCriticalsection.Leave;
end;


constructor TStringQueue.Create;
begin

inherited Create;
FItemlist := TStringList.Create;
FCriticalsection := TCriticalSection.Create;
end;


destructor TStringQueue.Destroy;
begin

inherited Destroy;
FItemlist.Destroy;
FCriticalsection.Destroy;
end;


function TStringQueue.Pop: string;
begin

FCriticalsection.Enter;
Result := FItemlist[0];
FItemlist.Delete(0);
FCriticalsection.Leave;
end;


procedure TStringQueue.Push(AItem: string);
begin

FCriticalsection.Enter;
FItemlist.Add(AItem);
FCriticalsection.Leave;
end;


function TStringQueue.GetAllItems: TStrings;
begin

FCriticalsection.Enter;
result := FItemlist;
FCriticalsection.Leave;
end;


end.
 
WorkerThread.pas

unit WorkerThread;

(*********************************************************************
* The contents of this file are used with permission, subject to *
* the Mozilla Public License Version 1.1 (the &quot;License&quot;);
you may *
* not use this file except in compliance with the License. You may *
* obtain a copy of the License at *
* http://www.mozilla.org/MPL/MPL-1.1.html *
* *
* Software distributed under the License is distributed on an *
* &quot;AS IS&quot;
basis, WITHOUT WARRANTY OF ANY KIND, either express or *
* implied. See the License for the specific language governing *
* rights and limitations under the License. *
* *
* (C) 2004 Martin Offenwanger: coder@dsplayer.de *
*********************************************************************)
{
@author(Martin Offenwanger: coder@dsplayer.de)
@created(Apr 22, 2004)
@lastmod(Sep 09, 2004)
}

interface

uses Windows, Classes, Asyncreader, config, ShoutCastStream, forms, baseclass;

// TAsyncIO FilePlayback instance
type
TWorkThread = class(TThread)
private
FIO: TAsyncIO;
protected
procedure Execute;
override;
public
constructor Create(AIO: TAsyncIO);
end;


// This is the Thread instance of the TShoutcastStream class
type
TThreadedShoutcastStream = class(TThread)
private
FMetaData: boolean;
FExitThread: boolean;
FTerminated: boolean;
FRipStream: boolean;
FFile: string;
FPath: string;
Factualpath: string;
FLock: TBCCritSec;
FAdress, FPort, FLocation: string;
protected
procedure Execute;
override;
public
constructor Create(Adress, Port, Location: string;
MetaData: boolean);
destructor Destroy;
override;
function SetRipStream(RipStream: boolean;
Path: string;
FileName: string): HRESULT;
function GetRipStream(out RipStream: boolean;
out Path: string): HRESULT;
end;


var
g_threadedShoutCastStream: TThreadedShoutcastStream;

implementation

constructor TWorkThread.Create(AIO: TAsyncIO);
begin

inherited Create(True);
FreeOnTerminate := false;
FIO := AIO;
end;


procedure TWorkThread.Execute;
begin

FIO.Process;
end;


destructor TThreadedShoutcastStream.Destroy;
var
Application: TApplication;
begin
// no need to protect this function
// a protect will cause a deadlock !
FExitThread := true;
Application := TApplication.Create(nil);
while not FTerminateddo

begin

Application.ProcessMessages;
Sleep(1);
end;

FLock.Free;
// freeandnil seems to be more savety here
inherited Destroy;
end;


// TThreadedShoutcastStream.get_ripStream is not implemented yet

function TThreadedShoutcastStream.GetRipStream(out RipStream: boolean;
out Path: string): HRESULT;
//var l_ripstream: boolean;
// l_path: string;
begin

FLock.Lock;
// protect our member objects
{ if g_shoutCastStream <> nil then
begin

g_shoutCastStream.get_ripStream(l_ripstream,l_path);
RipStream := l_ripstream;
Path := copy(l_path,1,length(l_path));
RESULT := S_OK;
end else
}
RESULT := E_FAIL;
FLock.UnLock;
end;


function TThreadedShoutcastStream.SetRipStream(RipStream: boolean;
Path: string;
FileName: string): HRESULT;
begin

FLock.Lock;
// protect our member objects
FRipStream := RipStream;
FPath := copy(Path, 1, system.length(Path));
FFile := copy(FileName, 1, system.length(FileName));
Result := S_OK;
FLock.UnLock;
end;


constructor TThreadedShoutcastStream.Create(Adress, Port, Location: string;
MetaData: boolean);
begin

inherited Create(false);
FLock := TBCCritSec.Create;
FMetaData := MetaData;
FRipStream := false;
FPath := '';
FExitThread := false;
FTerminated := false;
FAdress := Adress;
FPort := Port;
FLocation := Location;
end;


procedure TThreadedShoutcastStream.Execute;
var
Application: TApplication;
RipStream: boolean;
ShoutCastStream: TShoutcastStream;
Temp: string;
begin

FTerminated := false;
Temp := '';
ShoutCastStream := TShoutcastStream.Create;
ShoutCastStream.SetConnectToIp(FAdress, FPort, FLocation, FMetaData);
Application := TApplication.Create(nil);
// this is the mainloop of the tread
Priority := tpLowest;
while not FExitThreaddo

begin

FLock.Lock;
// protect our member objects
sleep(1);
Priority := tpTimeCritical;
Application.HandleMessage;
Priority := tpLowest;
ShoutCastStream.GetRipStream(RipStream, Temp);
if (RipStream <> FRipStream) or (Factualpath <> FPath) then

begin

ShoutCastStream.SetRipStream(FRipStream, FPath, FFile);
Factualpath := FPath;
end;

FLock.UnLock;
end;

if ShoutCastStream <> nil then

begin

ShoutCastStream.Destroy;
end;

FTerminated := true;
end;


end.
 
后退
顶部