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

基于JsSIP实现FreePBX监听/密语/插话功能时的DTMF问题

JsSIP对接FreePBX实现监听/密语/插话的问题与解决方案

FreePBX工作流程

根据FreePBX官方文档:

FreePBX功能码前缀可对指定分机执行监听/密语/插话操作。
使用方式:

  • 拨打带556前缀的本地分机以进入监听模式
  • 监听通话时,可通过以下DTMF输入切换模式:
    • DTMF 4:监听模式
    • DTMF 5:密语模式
    • DTMF 6:插话模式

当前实现

以下是我用JsSIP连接FreePBX并尝试实现密语功能的代码:

let ua;
let currentSession;
const SIP_DOMAIN = "your-domain-here";

const init = async () => {
  if (ua && ua.isConnected()) return;

  JsSIP.debug.disable("JsSIP:*");  

  const session = await getSession();
  if (!session) throw new Error("No active session found. Please log in.");

  const sipExtension = session.user.sip_config.sip_extension;
  const sipSecret = session.user.sip_config.sip_secret;

  if (!sipExtension || !sipSecret)
    throw new Error("SIP credentials not found in session.");

  const socket = new JsSIP.WebSocketInterface("wss://domain/ws");
  const configuration = {
    sockets: [socket],
    uri: `sip:${sipExtension}@${SIP_DOMAIN}`,
    password: sipSecret,
    display_name: "Client",
    // 补充媒体配置,确保DTMF兼容
    mediaConstraints: { audio: true, video: false },
    rtcpMuxPolicy: 'negotiate'
  };

  ua = new JsSIP.UA(configuration);

  ua.on("registered", () => {
    console.log("Connected to PBX");
  });

  ua.on("newRTCSession", (data) => {
    // 处理新会话,比如接听逻辑等
    const newSession = data.session;
    if (newSession.direction === 'incoming') {
      // 按需处理来电
    }
  });

  ua.start();
};

const whisperCall = async (sipConfig) => {
  console.log("Whispering to:", sipConfig);

  if (!ua)
    throw new Error("SIP user agent is not initialized. Call init first.");

  if (currentSession)
    throw new Error(
      "Another call is in progress. End the current call first."
    );

  // 关键:必须添加556前缀触发FreePBX监听逻辑
  const targetUri = `sip:556${sipConfig.sip_extension}@${SIP_DOMAIN}`;

  currentSession = ua.call(targetUri);

  currentSession.on("confirmed", () => {
    console.log("Call established, sending DTMF 5 for whisper mode");
    // 强制使用RFC2833发送DTMF,确保FreePBX识别
    currentSession.sendDTMF(5, { transportType: "RFC2833" });
  });

  // 监听会话失败事件
  currentSession.on("failed", (data) => {
    console.error("Whisper call failed:", data.cause);
    currentSession = null;
  });

  currentSession.on("ended", () => {
    currentSession = null;
  });

  return currentSession;
};

遇到的问题

  • 不确定发起通话时是否需要给分机添加556前缀,还是通过SIP URI其他结构处理
  • 通话建立后发送DTMF 5,FreePBX未识别,无法进入密语模式
  • 需要实现管理员向正在通话的坐席发起密语的功能

疑问解答与实现方案

1. 正确实现流程

严格遵循FreePBX的逻辑:

  • 第一步:拨打带556前缀的目标分机,触发监听模式(这是FreePBX识别操作的核心,普通拨号不会触发监听逻辑)
  • 第二步:通话建立后(confirmed事件触发),发送对应DTMF切换模式:
    • 发送4:切换到监听模式(仅管理员能听到通话)
    • 发送5:切换到密语模式(管理员和坐席能互相听到,客户听不到管理员)
    • 发送6:切换到插话模式(三方都能听到)

2. SIP URI的正确写法

必须在SIP URI中包含556前缀,即:

const targetUri = `sip:556${sipConfig.sip_extension}@${SIP_DOMAIN}`;

FreePBX是通过拨号前缀来识别监听请求的,普通拨号分机只会建立普通通话,无法触发后续的模式切换逻辑。

3. DTMF兼容配置

要确保JsSIP发送的DTMF能被FreePBX识别,需注意以下几点:

  • 强制使用RFC2833协议发送DTMF:在sendDTMF时明确指定transportType: "RFC2833",这是FreePBX默认优先支持的DTMF传输方式
  • UA配置中补充媒体相关设置:确保音频媒体正常协商,RTCP多路复用等配置正确(参考上面代码中的mediaConstraints和rtcpMuxPolicy)
  • 检查FreePBX端配置:在FreePBX的「SIP设置」→「全局设置」或目标分机的设置中,确认开启了RFC2833 DTMF支持,禁用或降低INFO包DTMF的优先级
  • 避免DTMF发送过早:必须在confirmed事件触发后再发送DTMF,此时通话已完全建立,媒体通道正常

4. 管理员密语坐席的权限配置

确保管理员使用的分机在FreePBX中有对应的权限:

  • 在FreePBX中进入「应用」→「权限管理」,给管理员分机添加「监听」「密语」「插话」相关的权限
  • 确认目标坐席的分机设置中,允许被监听/密语(部分FreePBX版本需要在分机设置中开启「允许监听」选项)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 00:59:57