使用ESL从FreeSWITCH获取通话时长失败,求可行实现方案
解决FreeSWITCH ESL获取通话时长的实现方案
问题根因
你当前只监听CHANNEL_EXECUTE_COMPLETE事件的逻辑存在偏差:这个事件仅在originate API执行完成时触发,此时呼叫还处于发起/振铃阶段,尚未接通也未结束,自然不会携带variable_answer_epoch、variable_end_epoch这类运行时变量。
实现方案
1. 调整ESL事件订阅范围
你需要额外订阅两类核心事件:
CHANNEL_ANSWER:呼叫接通时触发,会携带variable_answer_epoch变量CHANNEL_HANGUP_COMPLETE:呼叫完全挂断后触发,会携带variable_end_epoch、以及FreeSWITCH已自动计算好的时长变量variable_billsec(计费通话时长,单位:秒,即从接通到挂断的有效通话时长)、variable_duration(呼叫总时长,从发起创建到挂断的总时长,单位:秒)
订阅示例代码:
// ESL连接建立后订阅所需事件 connection.subscribe(['CHANNEL_EXECUTE_COMPLETE', 'CHANNEL_ANSWER', 'CHANNEL_HANGUP_COMPLETE'])
2. 基于呼叫UUID关联事件
你调用originate接口返回的callUid就是A腿(主叫侧)的唯一通道UUID,后续所有该呼叫相关事件都会携带对应UUID作为Channel-Unique-ID头部,你可以用这个ID作为键存储呼叫中间状态:
// 全局存储呼叫状态,生产环境可替换为Redis/数据库 const callMap = new Map() connection.on('esl::event', (event) => { const eventName = event.getHeader('Event-Name') const channelUuid = event.getHeader('Channel-Unique-ID') // 处理originate返回后的状态初始化 if (eventName === 'CHANNEL_EXECUTE_COMPLETE' && event.getHeader('Application') === 'originate') { callMap.set(callUid, { status: 'initiated' }) return } // 处理呼叫接通 if (eventName === 'CHANNEL_ANSWER' && callMap.has(channelUuid)) { const callInfo = callMap.get(channelUuid) callInfo.answerEpoch = event.getHeader('variable_answer_epoch') callInfo.status = 'answered' callMap.set(channelUuid, callInfo) return } // 处理呼叫挂断,计算时长 if (eventName === 'CHANNEL_HANGUP_COMPLETE' && callMap.has(channelUuid)) { const callInfo = callMap.get(channelUuid) // 方案1:直接取FreeSWITCH已计算好的计费时长,单位秒 const billSec = event.getHeader('variable_billsec') console.log('有效通话时长(秒):', billSec) // 方案2:用你原有逻辑自行计算 const endEpoch = event.getHeader('variable_end_epoch') const duration = calculateDuration(callInfo.answerEpoch, endEpoch) console.log('自行计算的通话时长(毫秒):', duration) // 清理已结束的呼叫状态 callMap.delete(channelUuid) return } })
3. 多腿呼叫关联优化(可选)
由于你是桥接两个外线号码,存在A、B两条呼叫腿,可在originate时添加自定义变量关联两条腿,避免事件匹配混乱:
// originate时添加自定义唯一标识 const customCallId = Date.now().toString() let arg1 = `{ignore_early_media=true,origination_caller_id_number=${callerId},x_custom_call_id=${customCallId}}${app_args}`;
后续A、B腿的所有事件都会携带variable_x_custom_call_id头部,可通过该字段将两次通道的事件匹配到同一次点击呼叫任务中。
内容的提问来源于stack exchange,提问作者venalyn sudaria
相关产品推荐
相关产品推荐

