import JsSIP from "jssip"; class SipService { constructor() { this.ua = null; this.currentSession = null; this.onStatusChange = null; // 状态变化回调 } // 初始化SIP客户端 init(config) { try { this.updateStatus("connecting", "连接中..."); this.ua = new JsSIP.UA({ sockets: [new JsSIP.WebSocketInterface(config.wsUrl)], uri: config.sipUri, password: config.password, display_name: config.displayName, iceservers:[], // realm: config.realm, register: true, session_expires: 180, sessionTimersExpires: 300, // 设置 Session-Expires=120(必须 >= Min-SE) extraHeaders: [ "Min-SE: 120", // 可选:显式告诉服务器你支持的最小值 ], register_expires: 300, // 注册有效期(秒) connection_recovery_min_interval: 2, // 最小重连间隔 connection_recovery_max_interval: 30, // 最大重连间隔 }); this.ua.start(); // 注册事件监听 this.ua.on("registered", () => { this.updateStatus("registered", "已注册"); }); this.ua.on("registrationFailed", (e) => { this.updateStatus("failed", `注册失败: ${e.cause}`); }); this.ua.on("disconnected", () => { this.updateStatus("disconnected", "连接断开"); }); this.ua.on("connected", () => { this.updateStatus("connecting", "重新连接中..."); }); // 监听来电 this.ua.on("newRTCSession", (data) => { this.handleIncomingCall(data.session); }); } catch (error) { this.updateStatus("failed", `初始化失败: ${error.message}`); console.error("SIP初始化失败:", error); } } handleIncomingCall(session) { if (session.direction === "incoming") { console.log("来电:", session.remote_identity.uri.toString()); // 可以在这里触发 UI 通知 if (this.onIncomingCall) { this.onIncomingCall(session); } } } // 更新状态并通知UI updateStatus(type, text) { console.log(`SIP状态更新: ${type} - ${text}`); if (this.onStatusChange) { this.onStatusChange({ type, text }); } } // 一键拨号 - 增加注册状态检查 makeCall(targetNumber) { if (!this.ua) { throw new Error("SIP客户端未初始化"); } if (!this.ua.isRegistered()) { throw new Error("SIP未注册,无法呼叫"); } const options = { sessionTimers: true, sessionTimersExpires: 300, extraHeaders: [ "Min-SE: 120", "Route: ", "Accept: application/sdp", "Supported: replaces, timer", "Allow: INVITE, ACK, BYE, CANCEL, OPTIONS", ], eventHandlers: { progress: (e) => console.log("呼叫中..."), failed: (e) => console.error("呼叫失败:", e), ended: (e) => console.log("通话结束"), confirmed: (e) => console.log("通话已接通"), }, mediaConstraints: { audio: true, video: false, }, rtcOfferConstraints: { offerToReceiveAudio: 1, offerToReceiveVideo: 0, mandatory: { OfferToReceiveAudio: true, OfferToReceiveVideo: false, }, }, pcConfig: { iceServers: [{ urls: "stun:stun.l.google.com:19302" }], iceTransportPolicy: "all", bundlePolicy: "balanced", rtcpMuxPolicy: "require", codecs: { audio: [ { name: "PCMU", clockRate: 8000, payloadType: 0 }, { name: "PCMA", clockRate: 8000, payloadType: 8 }, ], video: [], }, }, }; this.currentSession = this.ua.call( `sip:${targetNumber}@192.168.100.6`, options ); // 在会话创建后修改 SDP this.currentSession.on("peerconnection", (pc) => { pc.createOffer = (offerOptions) => { return RTCPeerConnection.prototype.createOffer .call(pc, offerOptions) .then((offer) => { const modifiedSdp = offer.sdp .replace(/c=IN IP4 192\.168\.100\.10/g, "c=IN IP4 192.168.100.6") .replace(/m=audio \d+ RTP\/AVP.*/, "m=audio 7078 RTP/AVP 0 8"); return new RTCSessionDescription({ type: "offer", sdp: modifiedSdp, }); }); }; }); this.setupAudio(this.currentSession); } setupAudio(session) { session.connection.addEventListener("addstream", (e) => { const audioElement = document.getElementById("remoteAudio"); if (audioElement) { audioElement.srcObject = e.stream; } }); } // 挂断当前通话 endCall() { if (this.currentSession) { this.currentSession.terminate(); this.currentSession = null; } } } export default new SipService();