You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的服务程序,可以用系统内核事件实现等待,逻辑更轻量:

  1. 给TTelefon新增私有成员FCallEvent: TEvent,构造函数中创建FCallEvent := TEvent.Create(nil, False, False, '');,析构函数中释放。
  2. Call方法发起呼叫后调用FCallEvent.WaitFor(INFINITE);等待。
  3. OnHangUp以及所有呼叫终止回调中调用FCallEvent.SetEvent;唤醒等待。

现有代码优化建议

  • 将全局变量SIP_Client、CallData改为TTelefon的私有成员,避免多实例调用时数据互相覆盖冲突。
  • 若需要等TTS播放完成再结束通话,需要监听PlayText的播放完成事件,播放完成后再触发通话结束标记,避免TTS未播完就被挂断。

内容的提问来源于stack exchange,提问作者MorrisF

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 15:15:06