{*******************************************************} { } { IAX Phone Component for Delphi } { by Andre Bierwirth bierwirth@kmb.de } { } { Copyright (c) 2002 KMB Software } { } {*******************************************************} unit IAX; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, iaxphone, iaxsnd, iaxgraph, mmsystem, dbugintf, winsock, extctrls, rxtimer,extdlgs; type TIAX = class; TIAXPeer = class; TIAXImageName = string; TIAXVideoOption = (ovSendImg,ovSendImgPre, ovEnableVid, ovIgnoreImg, ovIgnoreImgPre); TIAXVideoOptions = set of TIAXVideoOption; TIAXStatusEvent = procedure (Sender: TObject; Value: string) of object; TIAXLevelEvent = procedure (Sender: TObject; Value: Double) of object; TIAXPeerEventType = (ipeCreate, ipeDestroy, ipeTitle, ipeImage, ipeLinked, ipeStatus, ipeEnabled, ipeMute, ipeLagRep, ipeLevel, ipeConference, ipeHold, ipeCanAnswer, ipeURL); TIAXPeerEvent = procedure (Peer: TIAXPeer; ET: TIAXPeerEventType) of object; TIAXCallStart = procedure (Modal: Boolean; Dial: String) of object; TIAXCallEnd = TNotifyEvent; TIAXURLEvent = procedure (Sender: TObject; URL: string) of object; TIAXPeer = class(TCollectionItem) private FIAX: TIAX; FPcId: Integer; Fcid: string; FAddr: ADDR_EVENT; FHoldId: Integer; FTitle: string; FImage: TPicture; FLinked: Boolean; FEnabled: Boolean; FMute: Boolean; FLag: Integer; FJitter: Integer; FHistory: Double; FLevel: Double; FConference: Boolean; FHold: Boolean; FAnswer: Boolean; FStatus: string; FUrl: string; FLagLock: Boolean; FLagFd: TRxTimer; FFrame: TFrame; FUserData: Pointer; procedure SetTitle(Value: String); procedure SetStatus(Value: String); procedure SetLinked(Value: Boolean); procedure SetEnabled(Value: Boolean); procedure SetMute(Value: Boolean); procedure SetLevel(Value: Double); procedure SetConference(Value: Boolean); procedure SetHold(Value: Boolean); procedure SetAnswer(Value: Boolean); procedure SetURL(Value: String); procedure DoPeerEvent(ET: TIAXPeerEventType); procedure Send_Lag_Request(Sender: TObject); procedure HandleEvent(E: pc_event); procedure HandleHangup(Reason: string); procedure TimeoutHangup(Sender: TObject); procedure SendImage; procedure LoadImage(Data: Pointer; DataLen: Integer); public constructor Create(Collection: TCollection); override; destructor Destroy; override; procedure DoLinked(Value: Boolean); procedure DoAnswer; procedure DoHangup(why: string); property UserData: Pointer read FUserData Write FUserData; property IAX: TIAX read FIAX Write FIAX; published property PcId: Integer read FPcId write FPcId; property CId: string read FCid write FCid; property Addr: ADDR_EVENT read FAddr write FAddr; property HoldId: Integer read FHoldid Write FHoldid; property Title: string read FTitle write SetTitle; property Image: TPicture read FImage; property Linked: Boolean read FLinked Write SetLinked; property Enabled: Boolean read FEnabled Write SetEnabled; property Mute: Boolean read FMute Write SetMute; property Lag: Integer read FLag; property Jitter: Integer read FJitter; property Level: Double read FLevel Write SetLevel; property Conference: Boolean read FConference Write SetConference; property Hold: Boolean read FHold Write SetHold; property Answer: Boolean read FAnswer Write SetAnswer; property Status: string read FStatus write SetStatus; property URL: string read FURL Write SetURL; end; TIAXPeers = class(TCollection) public FIAX: TIAX; constructor Create(AOwner: TIAX); function FindPeer(PcId: Integer): TIAXPeer; end; TIAX = class(TComponent) private { Private-Deklarationen } FSThread: TThread; FCThread: TThread; FPort: Integer; FAudio: TIAXAudio; FPeers: TIAXPeers; FMostRecentAnswer: TIAXPeer; FCurrentPeer: TIAXPeer; FURLPeer: TIAXPeer; FLinkPcid: Integer; FRegistry: Integer; FSessions: Integer; FDefaultImage: TPicture; FVideoOptions: TIAXVideoOptions; FUserImage: TIAXImageName; FHistory: Double; FOnStatus: TIAXStatusEvent; FOnLevel: TIAXLevelEvent; FOnPeer: TIAXPeerEvent; FOnCallStart: TIAXCallStart; FOnCallEnd: TIAXCallEnd; FOnURL: TIAXURLEvent; procedure HandleEvent(e: pc_event); procedure SetPeerStatus(Peer: TIAXPeer; Value: string); procedure SelectPeer(Peer: TIAXPeer); // Audio Callbacks procedure OpenAudio; procedure CloseAudio; procedure ActivateAudio; procedure DeactivateAudio; procedure ConfigureAudio; procedure SetSpeedAudio(Speed: Integer); procedure SendAudio(Data: Pointer; Size: Integer); procedure SoundUpdate(Sender: TObject); protected { Protected-Deklarationen } procedure StartUrl(URL: string); procedure LaunchCall(Dest,Title: string; tbd: Boolean); procedure DigitReady(digit: Char; StartOk,UseUrl: Boolean); public { Public-Deklarationen } constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure DoRinging; procedure Call; procedure DoCall(calladdr: string); procedure Dial(callerid: string; destination: string; lang: string); procedure Dialdigit(Digit: char); procedure Entry(Location: string); function InterpretCall(s: string): Boolean; property Peers: TIAXPeers read FPeers; published { Published-Deklarationen } property DefaultImage: TPicture read FDefaultImage; property VideoOptions: TIAXVideoOptions read FVideoOptions write FVideoOptions; property UserImage: TIAXImageName read FUserImage Write FUserImage; property OnStatus: TIAXStatusEvent read FOnStatus write FOnStatus; property OnLevel: TIAXLevelEvent read FOnLevel write FOnLevel; property OnPeer: TIAXPeerEvent read FOnPeer write FOnPeer; property OnCallStart: TIAXCallStart read FOnCallStart Write FOnCallStart; property OnCallEnd: TIAXCallEnd read FOnCallEnd Write FOnCallEnd; property OnUrl: TIAXURLEvent read FOnUrl Write FOnUrl; end; procedure Register; const MAX_SESSIONS = 4; implementation uses DsgnIntf; procedure Debug(v: string); begin // WrDB(v); end; function GetEventStr(e: pc_event): string; begin case e.event of PC_EVENT_IMAGE: Result := 'PC_EVENT_IMAGE'; PC_EVENT_AUDIO: Result := 'PC_EVENT_AUDIO'; PC_EVENT_LOADCOMPLETE: Result := 'PC_EVENT_LOADCOMPLETE'; PC_EVENT_DTMF: Result := 'PC_EVENT_DTMF'; PC_EVENT_HANGUP: Result := 'PC_EVENT_HANGUP'; PC_EVENT_ANSWER: Result := 'PC_EVENT_ANSWER'; PC_EVENT_REJECT: Result := 'PC_EVENT_REJECT'; PC_EVENT_ACCEPT: Result := 'PC_EVENT_ACCEPT'; PC_EVENT_AUTHRQ: Result := 'PC_EVENT_AUTHRQ'; PC_EVENT_AUTHRP: Result := 'PC_EVENT_AUTHRP'; PC_EVENT_URL: Result := 'PC_EVENT_URL'; PC_EVENT_RINGA: Result := 'PC_EVENT_RINGA'; PC_EVENT_REGREP: Result := 'PC_EVENT_REGREP'; PC_EVENT_CONNECT: Result := 'PC_EVENT_CONNECT'; PC_EVENT_SELECT: Result := 'PC_EVENT_SELECT'; PC_EVENT_NEW: Result := 'PC_EVENT_NEW'; PC_EVENT_NEW_REPLY: Result := 'PC_EVENT_NEW_REPLY'; PC_EVENT_SELAUDIO: Result := 'PC_EVENT_SELAUDIO'; PC_EVENT_AUDIO_REPLY: Result := 'PC_EVENT_AUDIO_REPLY'; PC_EVENT_REGREQ: Result := 'PC_EVENT_REGREQ'; PC_EVENT_AUDIO_READY: Result := 'PC_EVENT_AUDIO_READY'; PC_EVENT_AUDIO_DIGIT: Result := 'PC_EVENT_AUDIO_DIGIT'; PC_EVENT_AUDIO_RING: Result := 'PC_EVENT_AUDIO_RING'; PC_EVENT_AUDIO_RINGING: Result := 'PC_EVENT_AUDIO_RINGING'; PC_EVENT_AUDIO_SHUTUP: Result := 'PC_EVENT_AUDIO_SHUTUP'; PC_EVENT_AUDIO_DEACTIVATE: Result := 'PC_EVENT_AUDIO_DEACTIVATE'; PC_EVENT_ADDR: Result := 'PC_EVENT_ADDR'; PC_EVENT_CONFERENCE: Result := 'PC_EVENT_CONFERENCE'; PC_EVENT_UNCONFERENCE: Result := 'PC_EVENT_UNCONFERENCE'; PC_EVENT_ONHOOK: Result := 'PC_EVENT_ONHOOK'; PC_EVENT_OFFHOOK: Result := 'PC_EVENT_OFFHOOK'; PC_EVENT_DIAL: Result := 'PC_EVENT_DIAL'; PC_EVENT_DIAL_TBD: Result := 'PC_EVENT_DIAL_TBD'; PC_EVENT_DPREQ: Result := 'PC_EVENT_DPREQ'; PC_EVENT_DPREP: Result := 'PC_EVENT_DPREP'; PC_EVENT_COMPLETE: Result := 'PC_EVENT_COMPLETE'; PC_EVENT_TRANSFER: Result := 'PC_EVENT_TRANSFER'; PC_EVENT_LAGREQ: Result := 'PC_EVENT_LAGREQ'; PC_EVENT_LAGREP: Result := 'PC_EVENT_LAGREP'; PC_EVENT_UNLINK: Result := 'PC_EVENT_UNLINK'; PC_EVENT_LINKREJECT: Result := 'PC_EVENT_LINKREJECT'; PC_EVENT_TEXT: Result := 'PC_EVENT_TEXT'; else Result := 'UNKNOWN_EVENT'; end; end; function Loudness(var History: double; Level: Integer): double; var sum: Double; begin sum:=Level; sum := sum / 32768; if sum > History then History := sum else History := sum/4 + 3.0 * History / 4; if History > 1 then History := 1; Result := History * 3; if Result > 1 then Result := 1; end; function is_phone_number(s: string): Boolean; const Digits = '()-,0123456789*# '; var I: Integer; begin Result := True; for I:=1 to Length(s) do begin if Pos(s[i],Digits)=0 then begin Result:=False; break; end; end; end; { ************* TIAXPeer ************* } constructor TIAXPeer.Create(Collection: TCollection); begin inherited Create(Collection); FIAX := TIAXPeers(Collection).FIAX; FImage := TPicture.Create; FImage.Assign(IAX.FDefaultImage); FEnabled := True; FAnswer := True; DoPeerEvent(ipeCreate); end; destructor TIAXPeer.Destroy; begin DoPeerEvent(ipeDestroy); FImage.Free; inherited Destroy; end; procedure TIAXPeer.SetTitle(Value: string); begin FTitle:=Value; DoPeerEvent(ipeTitle); end; procedure TIAXPeer.SetStatus(Value: string); begin FStatus:=Value; DoPeerEvent(ipeStatus); end; procedure TIAXPeer.SetLinked(Value: Boolean); begin if Value<>FLinked then begin FLinked:=Value; DoPeerEvent(ipeLinked) end; end; procedure TIAXPeer.SetEnabled(Value: Boolean); begin if Value<>FEnabled then begin FEnabled:=Value; DoPeerEvent(ipeEnabled) end; end; procedure TIAXPeer.SetConference(Value: Boolean); begin if Value<>FConference then begin FConference:=Value; DoPeerEvent(ipeConference) end; end; procedure TIAXPeer.SetMute(Value: Boolean); begin if Value<>FMute then begin FMute:=Value; DoPeerEvent(ipeMute) end; end; procedure TIAXPeer.SetLevel(Value: double); begin if Value<>FLevel then begin FLevel:=Value; DoPeerEvent(ipeLevel) end; end; procedure TIAXPeer.SetHold(Value: Boolean); begin if Value<>FHold then begin FHold:=Value; DoPeerEvent(ipeHold) end; end; procedure TIAXPeer.SetAnswer(Value: Boolean); begin if Value<>FAnswer then begin FAnswer:=Value; DoPeerEvent(ipeCanAnswer) end; end; procedure TIAXPeer.SetURL(Value: String); begin FURL:=Value; DoPeerEvent(ipeURL) end; procedure TIAXPeer.DoPeerEvent(ET: TIAXPeerEventType); begin if Assigned(FIAX.FOnPeer) then begin FIAX.FOnPeer(self,ET); end; end; procedure TIAXPeer.Send_Lag_Request(Sender: TObject); begin Debug('Send_Lag_Request'); // Lock the door FLagLock := True; if FLagFd=nil then begin FLagFd := TRxTimer.Create(application); FLagFd.Interval := 15000; FLagFd.OnTimer := Send_Lag_Request; end; // Send the lag request to the phonecore pc_send_lagreq(SOURCE_GUI, Fpcid); // Unlock the door FLagLock := False; end; procedure TIAXPeer.HandleEvent(E: pc_event); begin case E.event of PC_EVENT_ADDR: begin FAddr := E.addr; end; PC_EVENT_HANGUP: begin HandleHangup('Call disconnected by peer'); end; PC_EVENT_REJECT: begin if StrLen(e.reject.why)>0 then HandleHangup(Format('Call Rejected: %s',[e.reject.why])) else HandleHangup('Call Rejected: No Reason'); end; PC_EVENT_ACCEPT: begin Status:='Waiting for answer...'; if (ovSendImg in IAX.VideoOptions) and (ovSendImgPre in IAX.VideoOptions) then begin Debug('OUT: Before Send Image'); SendImage; Debug('OUT: After Send Image'); end; end; PC_EVENT_ANSWER: begin Status:='Call connected.'; IAX.SelectPeer(Self); Mute:=False; if ovSendImg in IAX.VideoOptions then SendImage; end; PC_EVENT_AUTHRQ: begin //TODO end; PC_EVENT_TRANSFER: begin if ovSendImg in IAX.VideoOptions then SendImage; end; PC_EVENT_IMAGE: begin if not (ovIgnoreImg in IAX.VideoOptions) then if E.image.datalen>0 then LoadImage(@E.image.data,E.image.datalen) else Debug('notice: empty image'); end; PC_EVENT_TEXT: begin //TODO end; PC_EVENT_URL: begin if (e.url.link>0) and (IAX.FLinkPcid<0) then begin // Send Request here IAX.FLinkPcid:=PcId; Linked:=True; end; if strLen(E.url.url)>0 then begin if (E.url.link>0) and (IAX.Flinkpcid<0) then Exit; URL:=StrPas(E.url.url); end; end; PC_EVENT_RINGA: begin Status:='Ringing...'; IAX.SetPeerStatus(nil,'Ringing...'); audio_ringing; // MessageBeep(MB_ICONEXCLAMATION); // MessageBeep(MB_ICONEXCLAMATION); // MessageBeep(MB_ICONEXCLAMATION); // AudioRinging(); end; PC_EVENT_AUDIO: begin FLevel := Loudness(FHistory, E.audio.level); DoPeerEvent(ipeLevel); E.event := PC_EVENT_AUDIO_READY; pc_write_event(SOURCE_GUI, e); end; PC_EVENT_LAGREP: begin FLag:=E.lag.lag; FJitter:=E.lag.jitter; DoPeerEvent(ipeLagRep); end; PC_EVENT_LINKREJECT: begin end; PC_EVENT_UNLINK: begin Linked:=False; IAX.FLinkPcid:=-1; end; else Debug(Format('Unknown event: %d\n', [e.event])); end; end; procedure TIAXPeer.HandleHangup(Reason: string); function GetOther: TIAXPeer; var I: Integer; P: TIAXPeer; begin Result := nil; for I:=0 to IAX.FPeers.Count-1 do begin P:=TIAXPeer(IAX.FPeers.Items[I]); if P<>Self then begin Result := P; end; end; end; begin Debug(Format('Handle Hangup pcid %d',[pcid])); // Got a hangup, destroy the peer */ // If we have linked browsers, let's close'em up */ if IAX.FLinkPcid = pcid then IAX.FLinkPcId := -1; if Assigned(FLagFd) then FreeAndNil(FLagFd); // We should close the conversation if there is one // // conversation_inactivate(p->pcid); audio_shutup; audio_deactivate; Collection := nil; if IAX.FMostRecentAnswer=Self then IAX.FMostRecentAnswer:=nil; if IAX.FCurrentPeer=Self then IAX.FCurrentPeer := GetOther; PcId := -1; // if HoldId>0 then HoldId:=-1; Status := Reason; dec(IAX.FSessions); Enabled := False; if (IAX.Fsessions < 0) then Debug(Format('Sessions is now %d?', [IAX.Fsessions])) else if (IAX.Fsessions > 0) then IAX.SetPeerStatus(nil, Format('%d active calls', [IAX.Fsessions])) else IAX.SetPeerStatus(nil,'Call disconnected.'); with TRxTimer.Create(nil) do begin Interval := 5000; OnTimer:=TimeoutHangup; end; end; procedure TIAXPeer.TimeoutHangup(Sender: TObject); begin Sender.Free; Free; end; procedure TIAXPeer.LoadImage(Data: Pointer; DataLen: Integer); var MS: TMemoryStream; F: TFileStream; DF: TGraphicClass; G: TGraphic; begin DF:=DetectFormat(Data,DataLen); if not Assigned(DF) then begin ShowMessage('Can´t Read Image (Unknown Format)'); Exit; end; MS:=TMemoryStream.Create; try MS.Write(Data^,DataLen); MS.Position:=0; G := DF.Create; try G.LoadFromStream(MS); FImage.Assign(G); DoPeerEvent(ipeImage); finally G.Free; end; finally MS.Free; end; end; procedure TIAXPeer.SendImage; var e: pc_event; F: TFileStream; begin if IAX.FUserImage='' then Exit; if not FileExists(IAX.FUserImage) then Exit; F:=TFileStream.Create(IAX.FUserImage,fmOpenRead); try if F.Size=0 then Exit; e.event:=PC_EVENT_IMAGE; e.len := SizeOf(pc_event); e.callno := pcid; if F.Size>SizeOf(e.image.data) then e.image.datalen := SizeOf(e.image.data) else e.image.datalen := F.Size; F.read(e.image.data, e.image.datalen); pc_write_event(SOURCE_GUI, e); finally F.Free; end; end; procedure TIAXPeer.DoLinked(Value: Boolean); begin if Value then begin IAX.FLinkPcid := PcId; FLinked:=True; end else begin pc_send_unlink(SOURCE_GUI,IAX.FLinkPcid); IAX.FLinkPcid:=-1; end; end; procedure TIAXPeer.DoAnswer; begin pc_answer(SOURCE_GUI, pcid); Mute := False; Answer:=False; Status := 'Connected.'; IAX.SetPeerStatus(nil,Format('%d active calls', [IAX.FSessions])); IAX.SelectPeer(Self); if IAX.FMostRecentAnswer = Self then IAX.FMostRecentAnswer := nil; // audio_shutup(); if (ovSendImg in IAX.FVideoOptions) then SendImage; end; procedure TIAXPeer.DoHangup(why: string); begin if why='' then why:='Goodbye.'; pc_hangup(SOURCE_GUI,PcId,@why[1]); HandleHangup('Call disconnected.'); end; { ************* TIAXPeers ************* } constructor TIAXPeers.Create(AOwner: TIAX); begin inherited Create(TIAXPeer); FIAX := AOwner; end; function TIAXPeers.FindPeer(PcId: Integer): TIAXPeer; var I: Integer; begin Result := nil; for I:=0 to Count-1 do begin if TIAXPeer(Items[I]).PcId=PcId then begin Result := TIAXPeer(Items[I]); Break; end; end; end; { ************* TIAXPhonecoreThread ************* } type TIAXPhonecoreThread = class(TThread) private public constructor Create; virtual; procedure Execute; override; end; constructor TIAXPhonecoreThread.Create; begin // Priority:=tpTimeCritical; inherited Create(False); end; procedure TIAXPhonecoreThread.Execute; begin phonecore_thread(nil); ShowMessage('Thread Dead'); end; { ************* TIAXGuiThread ************* } type TIAXGuiThread = class(TThread) private FIAX: TIAX; FEvent: pc_event; public constructor Create(AIAX: TIAX); virtual; procedure Execute; override; procedure HandleEvent; end; constructor TIAXGuiThread.Create(AIAX: TIAX); begin FIAX:=AIAX; // Priority:=tpTimeCritical; inherited Create(False); end; procedure TIAXGuiThread.Execute; var res: Integer; begin while true do begin res := pc_read_event(SOURCE_GUI, FEvent); if res<0 then begin ShowMessage('Failed to read from phonecore...'); end else Synchronize(HandleEvent); end; end; procedure TIAXGuiThread.HandleEvent; begin FIAX.HandleEvent(FEvent); end; { ************* TIAX ************* } procedure DebugCallback(Text: PChar); cdecl; begin // SendDebug(StrPas(Text)); end; procedure OpenAudioCallback(Instance: Pointer); cdecl; begin SendDebug('AUDIO Open'); if Assigned(Instance) then TIAX(Instance).OpenAudio; end; procedure CloseAudioCallback(Instance: Pointer); cdecl; begin SendDebug('AUDIO Close'); end; procedure ActivateAudioCallback(Instance: Pointer); cdecl; begin SendDebug('AUDIO Activate'); end; procedure DeactivateAudioCallback(Instance: Pointer); cdecl; begin SendDebug('AUDIO Deactivate'); end; procedure ConfigureAudioCallback(Instance: Pointer); cdecl; begin SendDebug('AUDIO Configure'); end; procedure SetSpeedAudioCallback(Instance: Pointer; Speed: Integer); cdecl; begin SendDebug('AUDIO SetSpeed '+IntToStr(Speed)); end; procedure SendAudioCallback(Instance: Pointer; Data: Pointer; Size: Longint); cdecl; begin if Assigned(Instance) then TIAX(Instance).SendAudio(Data, Size); end; procedure ReadAudioCallback(Instance: Pointer; Data: Pointer; var Size: Longint); cdecl; begin SendDebug('AUDIO Try Capture'); if Assigned(Instance) then begin TIAX(Instance).FAudio.Start; // Size:=320; // Data:=TIAX(Instance).FAudio.ReadBlock; end; end; constructor TIAX.Create(AOwner: TComponent); var wf: TWaveFormatEx; begin inherited Create(AOwner); FDefaultImage:=TPicture.Create; AssignDefaultImage(FDefaultImage); FVideoOptions := [ovSendImg, ovSendImgPre]; if not isIAXPhone then Exit; SendDebugClear; pc_debug_callback(DebugCallback); FPort := pc_init; FPeers := TIAXPeers.Create(self); FRegistry := -1; FLinkPcID := -1; FAudio := TIAXAudio.Create(Self); FAudio.OnUpdate := SoundUpdate; // setup the format for opening audio channels wf.wFormatTag := WAVE_FORMAT_PCM; wf.nChannels := 1; wf.nSamplesPerSec := 8000; wf.nAvgBytesPerSec := 16000; wf.nBlockAlign := 2; wf.wBitsPerSample := 16; wf.cbSize := 0; FAudio.Format := @wf; FSThread := TIAXPhonecoreThread.Create; FCThread := TIAXGuiThread.Create(self); sound_toapp_init(Self, OpenAudioCallback, CloseAudioCallback, ActivateAudioCallback, DeactivateAudioCallback, ConfigureAudioCallback, SetSpeedAudioCallback, SendAudioCallback, ReadAudioCallback); end; destructor TIAX.Destroy; begin try if not isIAXPhone then Exit; FAudio.Stop; FCThread.Suspend; FCThread.Terminate; FreeAndNil(FCThread); FSThread.Suspend; FSThread.Terminate; FreeAndNil(FSThread); FreeAndNil(FPeers); FAudio.Stop; FreeAndNil(FAudio); finally inherited Destroy; end; end; procedure TIAX.HandleEvent(e: pc_event); var peer: TIAXPeer; buf: string; buf2: string; Level: Double; begin // Debug('GUIEVENT = '+GetEventStr(e)); peer := FPeers.FindPeer(e.callno); if Assigned(Peer) then begin Peer.HandleEvent(e); Exit; end else begin if e.callno = -1 then begin case (e.event) of PC_EVENT_ONHOOK: begin // hangup(); // hide_call_window(); end; PC_EVENT_OFFHOOK: begin // answer(); end; PC_EVENT_DIAL: begin // digit_ready(e.e.dial.digit, 1, 0); end; PC_EVENT_AUDIO: begin Level:=loudness(FHistory, e.audio.level); if Assigned(FOnLevel) then FOnLevel(Self,Level); { if (other_loudness) then other_loudness(e.e.audio.level); if (self && self->level) gtk_progress_bar_update(GTK_PROGRESS_BAR(self->level), pos);} e.event := PC_EVENT_AUDIO_READY; pc_write_event(SOURCE_GUI, e); end; PC_EVENT_DIAL_TBD: // Did I need this ??? begin { show_call_window(0, 0); tbd_pcid = pc_session_new(); // FIXME: Make this not be hardcoded // if (pc_call(SOURCE_GUI, tbd_pcid, "Robbie", "tormenta:5036/TBD@default", NULL)) printf("DEBUG: TBD has been dialed.\n"); break; } end; PC_EVENT_DPREP: begin // Now, we should complete the call // //launch_call(e.e.dprep.number, e.e.dprep.number, 1); // And hide our call window */ //hide_call_window(); end; else begin ShowMessage('Don''t know what to do with oob event '+IntToStr(e.event)); end; end; // Case Exit; end; // Hier geht´s weiter Debug(Format('Event %d for callno %d, registry = %d', [e.event, e.callno, Fregistry])); if (e.callno=FRegistry) then begin FRegistry := -1; // TODO Registry Stuff here end; // This must be a new connection if (e.event <> PC_EVENT_CONNECT) then begin Debug(Format('Huh? This is an event for a non-existant session %d?', [e.callno])); Exit; end; Inc(Fsessions); if (FSessions >=MAX_SESSIONS) then begin SetPeerStatus(nil, 'Missed a call'); Exit; end; if ((StrLen(e.connect.callerid)>0) and (StrLen(e.connect.dnid)>0)) then begin buf := Format('Call from "%s" for "%s"', [e.connect.callerid, e.connect.dnid]); buf2 := e.connect.callerid; end else if StrLen(e.connect.dnid)>0 then begin buf := Format('Call for "%s"', [e.connect.dnid]); end else if StrLen(e.connect.callerid)>0 then begin buf := Format('Call from "%s"', [e.connect.callerid]); buf2 := e.connect.callerid; end else begin buf := Format('Call from %s', [inet_ntoa(e.connect.Addr.sin_addr)]); buf2 := inet_ntoa(e.connect.Addr.sin_addr); end; peer:=TIAXPeer(FPeers.Add); peer.PcId := e.callno; peer.Title := buf; peer.CId := buf2; peer.addr := e.addr; Application.ProcessMessages; { if (uc) stop_camera(); while(gtk_events_pending()) gtk_mainiteration_do(False); if (uc) start_camera(); } // Send an initial lag request */ Debug('IN: Have an New Call'); pc_accept(SOURCE_GUI, peer.PcId); Debug('IN: Have an Send an accept to New Call'); peer.send_lag_request(self); Debug('IN: Have an Send an LagReq to New Call'); // peer.DoPeerEvent(ipeImage); pc_ring_announce(SOURCE_GUI, peer.pcid); Debug('IN: Have an Send an Ringannounce to New Call'); FMostRecentAnswer := peer; // TODO if (not Assigned(FCurrentPeer)) then audio_ring(); setpeerstatus(nil, 'Incoming call!'); if (ovSendImg in VideoOptions) and (ovSendImgPre in VideoOptions) then begin Debug('IN: Before Send Image'); Peer.SendImage; Debug('IN: After Send Image'); end; end; end; procedure TIAX.SetPeerStatus(Peer: TIAXPeer; Value: string); begin if Assigned(Peer) then begin Debug('Peer Status: '+Value); end else begin if Assigned(FOnStatus) then FOnStatus(Self, Value); Debug('Main Status: '+Value); end; end; procedure TIAX.SelectPeer(Peer: TIAXPeer); begin if Assigned(FCurrentPeer) and (FCurrentPeer <> Peer) and (not Peer.Conference) then FCurrentPeer.Hold:=True; FCurrentPeer := Peer; if Assigned(FCurrentPeer) then pc_select(SOURCE_GUI, FCurrentPeer.pcid) else pc_select(SOURCE_GUI, -1); end; procedure TIAX.OpenAudio; begin FAudio.Start; end; procedure TIAX.CloseAudio; begin end; procedure TIAX.ActivateAudio; begin end; procedure TIAX.DeactivateAudio; begin end; procedure TIAX.ConfigureAudio; begin end; procedure TIAX.SetSpeedAudio(Speed: Integer); begin end; procedure TIAX.SendAudio(Data: Pointer; Size: Integer); begin FAudio.WriteBlock(Data); end; procedure TIAX.SoundUpdate(Sender: TObject); var Size: Integer; Buffer: Pointer; begin Buffer := FAudio.ReadBlock(Size); while Assigned(Buffer) do begin sound_toapp_send(Buffer, Size); FreeMem(Buffer); Buffer := FAudio.ReadBlock(Size); end; end; procedure TIAX.DoRinging; begin audio_ring; end; procedure TIAX.StartUrl(URL: string); begin if Assigned(FOnURL) then FOnURL(Self, URL); end; procedure TIAX.LaunchCall(Dest,Title: string; tbd:Boolean); var Peer: TIAXPeer; buf: array [0..79] of Char; CallerId: string; pcid: Integer; begin // If we´re doing a tbd call, lets use call complete if Length(Dest)=0 then Exit; if tbd then begin { No Tbd Support now if (pc_complete(SOURCE_GUI, tbd_pcid, @Dest[1])=-1) then begin SetPeerStatus(nil, Format('Invalid destination: %s', [Dest])); Exit; end; pcid := tbd_pcid; tbd_pcid := -1; } end else begin // Otherwise, let's just make a new call pcid := pc_session_new(); gethostname(buf,SizeOf(buf)); CallerId := StrPas(buf); if CallerId='' then CallerId:='Gnophone User'; if (pc_call(SOURCE_GUI, pcid, @CallerId[1], @Dest[1], nil)=-1) then begin SetPeerStatus(nil, Format('Invalid destination: %s', [Dest])); Exit; end; Peer:=TIAXPeer(FPeers.Add); Peer.PcId:=pcid; Peer.CId:=Title; Peer.Title:=Format('Call to %s',[Title]); Inc(Fsessions); Peer.Send_Lag_Request(Self); Peer.DoPeerEvent(ipeImage); Peer.Answer:=False; SetPeerStatus(nil, 'Attempting connection'); SelectPeer(Peer); end; end; procedure TIAX.DigitReady(digit: Char; StartOk,UseUrl: Boolean); begin end; procedure TIAX.Call; begin if Assigned(FOnCallStart) then FOnCallStart(True,''); end; procedure TIAX.DoCall(calladdr: string); begin if Length(calladdr)>0 then begin if Assigned(FOnCallEnd) then FOnCallEnd(Self); if Pos('://',calladdr)=0 then begin if not is_phone_number(calladdr) then begin if Pos('.',calladdr)<>0 then calladdr := 'iax://'+calladdr; end; if InterpretCall(calladdr) then SetPeerStatus(nil,'Invalid call address'); end; end; end; procedure TIAX.Dial(callerid: string; destination: string; lang: string); var pcid: Integer; begin if callerid='' then callerid := 'IAXphone'; pcid := pc_session_new; pc_call(SOURCE_GUI, pcid, @callerid[1], @destination[1], @lang[1]); end; procedure TIAX.Dialdigit(Digit: char); begin //send_digit(Digit); if Assigned(FCurrentPeer) and (FCurrentPeer.FPcId>-1) then begin Debug('Sending Digit...'+Digit); pc_send_dtmf(SOURCE_GUI, FCurrentPeer.PcId, Digit); end else begin if Assigned(FOnCallStart) then FOnCallStart(False,Digit); end; end; procedure TIAX.Entry(Location: string); begin if Length(Location)>0 then begin InterpretCall(Location); if FLinkPcid<>-1 then begin pc_send_linked_url(SOURCE_GUI, FLinkPcid, @Location[1]); end; end; end; function TIAX.InterpretCall(s: string): Boolean; var I: Integer; B: Boolean; ihr: string; proto, dest: string; function cmp(src,dst:string):Boolean; begin Result := CompareText(src,dst)=0; end; begin Result := True; // Error True; // Try to interpret what they type in the space... */ if Pos(' ',s)<>0 then begin // It's probably a name... Maybe one day we'll look it // up in their address book */ Exit; end; if Pos(':',s)<>0 then begin // It's either a URL, or an IAX string */ proto:=''; dest:=''; B :=False; for i:=1 to Length(s) do begin if B then dest:=dest+s[i] else if s[i]=':' then B:=True else proto:=proto+s[i]; end; if cmp(proto,'http') or cmp(proto,'https') or cmp(proto,'ftp') or cmp(proto,'mailto') or cmp(proto,'about') or cmp(proto,'file') or cmp(proto,'finger') or cmp(proto,'keyword') then begin StartUrl(s); end else if cmp(proto, 'iax') and (Length(dest)>3) then begin proto:=Copy(s,7,9999); LaunchCall(proto, proto, false); end else if cmp(proto,'dtmf') and (Length(dest)>0) then begin // for I:=1 to dest do // digit_ready(dest[I],False,True); end else if cmp(proto, 'gnophone') then begin // An internal gnophone thingy if cmp(dest,'audiocontrol') then // show_audio_control() else if cmp(dest,'videocontrol') then // image_callback() else if cmp(dest,'telephonecontrol') then // telco_prefs; end else begin if is_phone_number(s) then begin Debug(Format('%s is a phone number', [s])); // ihr := telco_to_irl(s); if (ihr<>'') then begin Debug(Format('Using: %s', [ihr])); // launch_call(ihr, s, false); end else begin Debug(Format('Couldn''t make it: %s', [ihr])); Result := True; Exit; end; end else begin // ihr := telco_to_irl(s); if (ihr<>'') then begin Debug(Format('Using: %s', [ihr])); // launch_call(ihr, s, false); end else begin Debug(Format('Couldn''t make it: %s', [ihr])); Result:=True; Exit; end; end; end; Result := False; end; end; type TIAXImageNameProperty = class(TStringProperty) protected public procedure Edit; override; function GetAttributes: TPropertyAttributes; override; end; { TIAXImageNameProperty } procedure TIAXImageNameProperty.Edit; var FileOpen: TOpenPictureDialog; begin FileOpen := TOpenPictureDialog.Create(Application); try FileOpen.Filename := GetValue; FileOpen.InitialDir := ExtractFilePath(FileOpen.Filename); FileOpen.Options := [ofHideReadOnly, ofFileMustExist]; FileOpen.DefaultExt := GraphicExtension(TGraphic); FileOpen.Filter := GraphicFilter(TGraphic); if FileOpen.Execute then SetValue(FileOpen.Filename); finally FileOpen.Free; end; end; function TIAXImageNameProperty.GetAttributes: TPropertyAttributes; begin Result := [paDialog {$IFDEF WIN32}, paRevertable {$ENDIF}]; end; procedure Register; begin RegisterComponents('IAX', [TIAX]); RegisterPropertyEditor(TypeInfo(TIAXImageName), TIAX, '', TIAXImageNameProperty); end; initialization finalization end.