基于PJSUA/PJSIP如何检测被叫方拒接呼叫并处理异常
解决PJSUA/PJSIP被叫拒接时无法正确捕获状态的问题
核心排查与处理方案
被叫拒接却触发PJSIP_INV_STATE_CONFIRMED,本质是运营商/网关的特殊处理逻辑:拒接时先返回200 OK建立媒体通道,通过播放提示音告知呼叫失败,而非直接返回486(用户忙)、603(拒绝)这类错误响应。这种场景下不能仅依赖on_call_state的状态变更,需结合以下优雅方案处理:
1. 监听SIP错误响应与Reason头
在on_call_rx_msg回调中解析收到的SIP消息,捕获错误状态码或Reason头来判断拒接:
static void on_call_rx_msg(pjsua_call_id call_id, pjsip_rx_data *rdata) { pjsip_msg *msg = rdata->msg_info.msg; if (PJSIP_IS_RESPONSE(msg)) { pjsip_status_code status = PJSIP_MSG_STATUS_CODE(msg); if (status >= 400) { // 检查Reason头是否包含拒接/忙线标识 pjsip_generic_string_hdr *reason_hdr = (pjsip_generic_string_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_REASON, NULL); if (reason_hdr && (strstr(reason_hdr->hvalue.ptr, "busy") || strstr(reason_hdr->hvalue.ptr, "rejected"))) { pjsua_call_hangup(call_id, 0, NULL, NULL); return; } // 直接根据标准错误状态码处理 if (status == 486 || status == 603) { pjsua_call_hangup(call_id, 0, NULL, NULL); } } } }
2. 监控媒体通道无活动状态
播放完拒接提示音后,媒体通道会进入长时间静音状态,可在on_call_media_state中检测并结束呼叫:
static void on_call_media_state(pjsua_call_id call_id) { pjsua_call_info ci; pjsua_call_get_info(call_id, &ci); if (ci.media_status == PJSUA_CALL_MEDIA_ACTIVE) { pjsua_conf_port_info conf_info; pjsua_conf_get_port_info(ci.conf_slot, &conf_info); static pj_time_val last_inactive_ts; pj_time_val now; pj_gettimeofday(&now); // 检测端口是否持续静音(阈值可根据需求调整) if (conf_info.tx_level < -60 && conf_info.rx_level < -60) { if (last_inactive_ts.sec == 0) { last_inactive_ts = now; } else { pj_int32_t diff = pj_time_val_subtract(&now, &last_inactive_ts); if (diff >= 3000) { // 持续3秒无活动 pjsua_call_hangup(call_id, 0, NULL, NULL); last_inactive_ts.sec = 0; } } } else { last_inactive_ts.sec = 0; } } }
3. 补充on_call_state的状态判断逻辑
即使收到PJSIP_INV_STATE_CONFIRMED,也要结合呼叫信息中的最后状态码做校验:
static void on_call_state(pjsua_call_id call_id) { pjsua_call_info ci; pjsua_call_get_info(call_id, &ci); // 覆盖特殊场景:状态为确认但实际收到错误响应 if (ci.state == PJSIP_INV_STATE_CONFIRMED && ci.last_status >= 400) { pjsua_call_hangup(call_id, 0, NULL, NULL); return; } // 原有状态处理逻辑... }
额外调试建议
- 用
pjsua --log-level 5开启详细SIP日志,查看被叫拒接时实际收到的消息内容,确认网关的处理逻辑。 - 部分网关会在提示音播放后主动发送BYE消息,此时
on_call_state会收到PJSIP_INV_STATE_DISCONNECTED,无需额外处理。
内容的提问来源于stack exchange,提问作者Olejan
相关产品推荐
相关产品推荐

