Delphi开发SIP电话类时如何等待通话完成后再释放类实例
Delphi SIP类同步等待通话结束解决方案
问题根源
当前Call方法为异步执行逻辑,SIP拨号、呼叫建立、通话状态变更全依赖事件回调驱动,Call方法发起呼叫后会立即返回,导致代码直接执行finally块释放TTelefon实例,析构函数中会销毁SIP客户端对象,直接中断未完成的通话。
同时现有代码将SIP_Client、CallData定义为全局变量,存在多实例调用数据冲突的隐患。
实现同步等待的方案
要实现类似ShowModal的阻塞等待直到通话结束的效果,推荐两种实现方式:
方案1:消息循环等待(适配UI场景,不卡死界面)
第一步:修改TTelefon类定义
新增私有成员标记通话结束状态:
type TTelefon = class(TObject) private FCallFinished: Boolean; // 新增:标记通话是否结束 // 原有其他私有成员... // 原有其他定义... end;
第二步:修改OnHangUp回调
通话结束时标记状态:
procedure TTelefon.OnHangUp(Sender: TObject; const aCall: ICall); begin inherited; try if Assigned(aCall) then CallData.Call := aCall; FCallConnected := False; FCallFinished := True; // 新增:标记通话结束 except on e: Exception do ShowMessage(e.Message); end; end;
第三步:修改Call方法,新增等待逻辑
发起呼叫后进入消息循环等待,直到通话结束:
function TTelefon.Call(aNumber: string; aTTS: Boolean=False; aTTSText: String=''): Boolean; var tmpOK: Boolean; begin try tmpOK := False; FCallFinished := False; // 初始化通话结束标记 if not FServerConnected then FServerConnected := Connect(Server, User, Pass); if FServerConnected then begin CallData.Number := aNumber; CallData.TTS := aTTS; CallData.TTSText := aTTSText; if not Assigned(CallData.Call) then CallData.Call := SIP_Client.Call(CallData.Number) else if CallData.Call.State = csRinging then CallData.Call.Answer(); tmpOK := True; end; // 新增:同步等待逻辑 if tmpOK then begin while not FCallFinished do begin Application.ProcessMessages; // 处理UI消息,避免界面卡死 Sleep(10); // 降低CPU占用 // 可选:支持ESC键主动挂断退出等待 if GetAsyncKeyState(VK_ESCAPE) <> 0 then begin HangUp; Break; end; end; end; Result := tmpOK; except on e: Exception do ShowMessage(e.Message); end; end;
补充异常场景处理
需要给SIP客户端的呼叫失败、对方拒接、无人接听等事件绑定回调,所有呼叫终止的场景都要设置FCallFinished := True,避免无限等待。
方案2:内核事件等待(适配无UI服务场景)
如果是无UI的服务程序,可以用系统内核事件实现等待,逻辑更轻量:
- 给TTelefon新增私有成员
FCallEvent: TEvent,构造函数中创建FCallEvent := TEvent.Create(nil, False, False, '');,析构函数中释放。 Call方法发起呼叫后调用FCallEvent.WaitFor(INFINITE);等待。OnHangUp以及所有呼叫终止回调中调用FCallEvent.SetEvent;唤醒等待。
现有代码优化建议
- 将全局变量
SIP_Client、CallData改为TTelefon的私有成员,避免多实例调用时数据互相覆盖冲突。 - 若需要等TTS播放完成再结束通话,需要监听
PlayText的播放完成事件,播放完成后再触发通话结束标记,避免TTS未播完就被挂断。
内容的提问来源于stack exchange,提问作者MorrisF
相关产品推荐
相关产品推荐

