使用PJSUA2建立SIP呼叫时媒体会话创建失败(PJ_EBUSY)求助
PJSUA2媒体会话创建失败(PJ_EBUSY/70011)排查解决
我使用PJSUA2与某应用建立完整SIP呼叫,需执行流程:接听呼叫→播放消息→等待我方播放消息→回复播放另一消息后挂断。目前呼叫已连接至服务器(可查看呼叫ID及相关日志),但媒体会话无法创建,报错信息为Unable to create media session: Object is busy (PJ_EBUSY) [status=70011]。
相关日志流程:
- 我方发送INVITE请求
- 收到100 TRYING响应
- 收到200 OK响应
- SDP协商成功
- 呼叫进入CONNECTING状态
- 随即抛出上述媒体会话创建失败错误
以下是运行的Python 3.11代码:
import pjsua2 as pj import time import random endpoint_config = pj.EpConfig() endpoint_config.logConfig.level = 9 endpoint_config.medConfig.sndClockRate = 8000 endpoint = pj.Endpoint() endpoint.libCreate() endpoint.libInit(endpoint_config) print("Endpoint initialized..") # Create SIP transport sip_transport_config = pj.TransportConfig() transport_type = pj.PJSIP_TRANSPORT_TCP sip_transport_config.port = 5060 endpoint.transportCreate(transport_type, sip_transport_config) print("Transport created..") # Start the library endpoint.libStart() print("Endpoint started..") endpoint.libHandleEvents(10) # Subclass to extend the Account and get notifications etc. class MyAccount(pj.Account): def __init__(self): super().__init__() def onRegState(self, prm): ai = self.getInfo() print("*** Register: code=" if ai.regIsActive else "*** Unregister: code=", prm.code) class MyCall(pj.Call): def __init__(self, account, call_id): super().__init__(account, call_id) self.acc = account self.connected = False def onCallState(self, prm): time.sleep(5) print(f"THE STATE OF THE CALL HAS CHANGED: {prm}") call_info = self.getInfo() self.connected = call_info.state == pj.PJSIP_INV_STATE_CONFIRMED print("CONNECTED:", self.connected) print("current state is " + str(call_info.state) + " " + call_info.stateText) print("GET ID:", self.getId()) print("HAS MEDIA:", self.hasMedia()) def onMediaState(self): print("ON MEDIA STATE") # pjsua2 test function def pj_test(): account_config.idUri = "sip:111@111.111.1.1" account = MyAccount() try: account.create(account_config) print("ACCOUNT?", account) print("\nAccount created/registered..\n") except pj.Error as err: print("ERR acc:",err) call = MyCall(account, pj.PJSUA_INVALID_ID) prm = pj.CallOpParam(True) try: print("Attempting to make a call...") # USE prm.txOption – Optional headers etc to be added to outgoing INVITE request. call.makeCall("sip:+12011124111@sip.address:5060;transport=tcp", prm) ## Dev print("CALLINFO",callinfo := call.getInfo()) print("STATE:", callinfo.state) print("CALLID:", callinfo.id) print("GET ID:", call.getId()) print("HAS MEDIA:", call.hasMedia()) except Exception as err: print("ERR:",err) # call.dump() endpoint.libDestroy() if __name__ == "__main__": pj_test()
排查思路与解决方法
1. 检查资源占用冲突
PJ_EBUSY通常意味着媒体资源(音频设备、端口)被占用:
- 确认系统中无其他进程占用音频输入/输出设备(如浏览器、其他通话软件);
- 用
netstat -ano(Windows)或ss -tulpn(Linux)检查SDP协商的RTP端口及5060端口是否被占用。
2. 修复过早销毁Endpoint的问题
代码中调用call.makeCall()后立刻执行endpoint.libDestroy(),此时呼叫异步流程未完成,媒体资源被强制销毁导致冲突:
- 替换原有的
endpoint.libDestroy(),添加事件循环监听呼叫状态,直到通话结束再销毁资源:# 替换原来的endpoint.libDestroy() while True: endpoint.libHandleEvents(10) call_info = call.getInfo() if call_info.state == pj.PJSIP_INV_STATE_DISCONNECTED: print("Call disconnected, exiting...") break time.sleep(0.1) endpoint.libDestroy()
3. 移除回调中的阻塞操作
onCallState里的time.sleep(5)会阻塞PJSUA2事件处理线程,导致媒体会话创建超时:
- 直接移除该睡眠语句,改用状态标记做后续异步处理。
4. 优化媒体配置
- 确认
medConfig.sndClockRate与远端协商的媒体时钟速率一致,若远端用16000Hz,本地8000Hz可能导致初始化失败; - 显式指定RTP端口范围避免冲突:
endpoint_config.medConfig.rtpPortRange = pj.PjPortRange(5000, 5100)
5. 启用更详细的媒体日志
将日志级别调至10,重点查看pjmedia相关日志,定位具体被占用的资源:
endpoint_config.logConfig.level = 10
内容的提问来源于stack exchange,提问作者Barns89
相关产品推荐
相关产品推荐

