如何为PJSUA2 v2.13的SIP VoIP音频通话实现视频功能?
PJSUA2 2.13 + Qt Python 扩展视频通话实现方案
一、核心配置修改
首先在Endpoint初始化阶段启用视频支持,配置基础参数:
from pjsua2 import * # 初始化Endpoint ep = Endpoint() ep.libCreate() ep_config = EpConfig() # 启用视频并配置设备 ep_config.mediaConfig.enableVideo = True ep_config.mediaConfig.videoConfig.defaultCaptureDev = -1 # 使用系统默认摄像头 ep_config.mediaConfig.videoConfig.defaultRenderDev = -1 # 使用系统默认渲染设备 # 设置视频编码优先级(H264兼容性更广,按需调整) ep_config.mediaConfig.videoCodecs = ["H264/90000", "VP8/90000"] # 初始化库 ep.libInit(ep_config) # 可选:枚举并指定摄像头 vid_dev_mgr = ep.vidDevManager() capture_devs = vid_dev_mgr.enumCaptureDev() if capture_devs: ep_config.mediaConfig.videoConfig.defaultCaptureDev = capture_devs[0].id
二、Qt视频渲染控件实现
自定义QWidget子类处理视频帧转换与UI渲染,确保线程安全更新:
from PyQt5.QtWidgets import QWidget from PyQt5.QtGui import QImage, QPainter from PyQt5.QtCore import Qt, QMetaObject class VideoDisplay(QWidget): def __init__(self, parent=None): super().__init__(parent) self._current_frame = None def update_frame(self, pj_frame): # 转换PJSUA2的YUV420P帧到QImage width = pj_frame.width height = pj_frame.height # 提取帧数据 y_buf = pj_frame.buf[0].read().bytes u_buf = pj_frame.buf[1].read().bytes v_buf = pj_frame.buf[2].read().bytes # 初始化RGB32格式QImage img = QImage(width, height, QImage.Format_RGB32) ptr = img.bits() ptr.setsize(img.byteCount()) # 注意:实际生产环境需用高效YUV转RGB算法(可参考pjmedia_yuv2rgb或OpenCV) # 此处省略具体转换逻辑,仅保留框架 self._current_frame = img # 强制在Qt主线程更新UI QMetaObject.invokeMethod(self, "update", Qt.QueuedConnection) def paintEvent(self, event): if self._current_frame: painter = QPainter(self) # 按比例缩放渲染视频 painter.drawImage(self.rect(), self._current_frame.scaled(self.size(), Qt.KeepAspectRatio))
三、Call类扩展视频回调
继承Call类,重写视频帧接收方法,绑定Qt渲染控件:
class SIPCall(Call): def __init__(self, account, call_id=PJSUA_INVALID_ID): super().__init__(account, call_id) self.video_widget = None # 关联Qt视频显示控件 def onVideoFrameReceived(self, prm, frame): # 收到视频帧时更新绑定的控件 if self.video_widget: self.video_widget.update_frame(frame)
四、呼叫与接听的视频参数配置
发起或接听呼叫时,必须明确启用视频流:
发起视频呼叫
# 假设已初始化Account实例acc call_op_param = CallOpParam() call_op_param.opt.audioCount = 1 call_op_param.opt.videoCount = 1 # 启用视频流 call_op_param.opt.streamFlags = PJSUA2_STREAM_FLAG_VIDEO call = SIPCall(acc) # 绑定本地视频显示控件 call.video_widget = self.local_video call.makeCall("sip:target@your-sip-server.com", call_op_param)
接听视频呼叫
class SIPAccount(Account): def onIncomingCall(self, prm): call = SIPCall(self, prm.callId) answer_param = CallOpParam() answer_param.opt.audioCount = 1 answer_param.opt.videoCount = 1 call.answer(answer_param) # 绑定远端视频显示控件 call.video_widget = self.parent().findChild(VideoDisplay, "remote_video")
五、常见问题排查
- 视频支持未启用:确认PJSUA2编译时添加
--enable-video参数,依赖库(如ffmpeg、v4l2)已正确安装 - 摄像头权限:Linux下确保用户属于
video组,macOS/Windows需在系统隐私设置中允许应用访问摄像头 - 编码协商失败:查看PJSUA2日志(
ep.libLogWrite()输出),确认双方支持的视频编码一致 - UI更新异常:视频帧回调运行在PJSUA2线程,必须用
QMetaObject.invokeMethod在Qt主线程更新控件 - NAT穿越问题:视频流需更大带宽,确保STUN/TURN服务器配置正确,可通过
ep_config.uaConfig.stunServer设置
内容的提问来源于stack exchange,提问作者Marcello Romagnoli
相关产品推荐
相关产品推荐

