From 071e3ed6253ce4d87f6c7a58d7cab02bb3916373 Mon Sep 17 00:00:00 2001 From: WXL (wul) <wl_5969728@163.com> Date: 星期三, 17 九月 2025 11:43:20 +0800 Subject: [PATCH] 电话更新 --- src/utils/sipService.js | 325 ++++++++++++++++++++++------------------------------- 1 files changed, 136 insertions(+), 189 deletions(-) diff --git a/src/utils/sipService.js b/src/utils/sipService.js index dbc4a4e..1654ccc 100644 --- a/src/utils/sipService.js +++ b/src/utils/sipService.js @@ -4,211 +4,174 @@ constructor() { this.ua = null; this.currentSession = null; - this.onStatusChange = null; - this.onCallStatusChange = null; - this.onIncomingCall = null; - this.isRegistered = false; // 鏂板娉ㄥ唽鐘舵�佹爣蹇� - this.registrationTime = null; // 鏂板娉ㄥ唽鎴愬姛鏃堕棿鎴� + this.onStatusChange = null; // 鐘舵�佸彉鍖栧洖璋� + this.onCallStatusChange = null; // 鏂板閫氳瘽鐘舵�佸洖璋� } + // 鍒濆鍖朣IP瀹㈡埛绔� init(config) { try { - this.updateStatus("connecting", "杩炴帴涓�;..."); + 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: [], + iceservers: [], + // realm: config.realm, register: true, - sessionExpires: 1800, - minSessionExpires: 90, - register_expires: 300, + 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.isRegistered = true; - this.registrationTime = Date.now(); // 璁板綍娉ㄥ唽鎴愬姛鏃堕棿 - console.log(this.registrationTime, "娉ㄥ唽鏃堕棿"); - this.updateStatus("registered", "宸叉敞鍐�"); }); this.ua.on("registrationFailed", (e) => { - this.isRegistered = false; this.updateStatus("failed", `娉ㄥ唽澶辫触: ${e.cause}`); }); - this.ua.on("unregistered", () => { - this.isRegistered = false; - let registrationTime = Date.now(); // 璁板綍娉ㄩ攢鎴愬姛鏃堕棿 - console.log(registrationTime, "娉ㄩ攢鏃堕棿"); - this.updateStatus("disconnected", "宸叉敞閿�"); + this.ua.on("disconnected", () => { + this.updateStatus("disconnected", "杩炴帴鏂紑"); }); - this.ua.on("disconnected", () => - this.updateStatus("disconnected", "杩炴帴鏂紑") - ); - this.ua.on("connected", () => - this.updateStatus("connecting", "閲嶆柊杩炴帴涓�...") - ); - this.ua.on("newRTCSession", (data) => - this.handleIncomingCall(data.session) - ); + + 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); - throw error; } } - // 鏂板鏂规硶锛氭鏌ユ槸鍚﹀彲浠ュ懠鍙� - canMakeCall(minDelay = 2000) { - if (!this.isRegistered) { - return { canCall: false, reason: "SIP鏈敞鍐岋紝鏃犳硶鍛煎彨" }; - } - - const now = Date.now(); - const timeSinceRegistration = now - this.registrationTime; - - if (timeSinceRegistration < minDelay) { - const remaining = minDelay - timeSinceRegistration; - return { - canCall: false, - reason: `娉ㄥ唽鎴愬姛锛岃绛夊緟 ${Math.ceil(remaining / 1000)} 绉掑悗鍐嶅懠鍙玚, - }; - } - - return { canCall: true, reason: "" }; - } - makeCall(targetNumber) { - const { canCall, reason } = this.canMakeCall(); - if (!canCall) { - return Promise.reject(new Error(reason)); - } - return new Promise((resolve, reject) => { - try { - if (!this.ua) { - throw new Error("SIP瀹㈡埛绔湭鍒濆鍖�"); - } - - if (!this.ua.isRegistered()) { - throw new Error("SIP鏈敞鍐岋紝鏃犳硶鍛煎彨"); - } - - const options = { - sessionTimers: true, // 鍚敤浼氳瘽璁℃椂鍣� - sessionTimersExpires: 150, - extraHeaders: ["Accept: application/sdp"], - mediaConstraints: { audio: true, video: false }, - rtcOfferConstraints: { - offerToReceiveAudio: true, - offerToReceiveVideo: false, - }, - eventHandlers: { - progress: () => this.updateCallStatus("calling", "鍛煎彨涓�..."), - failed: (e) => { - this.handleCallFailure(e, reject); - }, - ended: () => this.updateCallStatus("ended", "閫氳瘽缁撴潫"), - confirmed: () => { - this.updateCallStatus("connected", "閫氳瘽宸叉帴閫�"); - resolve(); - }, - }, - }; - - this.currentSession = this.ua.call( - `sip:${targetNumber}@1192.170.66.107`, - options - ); - - this.setupPeerConnection(this.currentSession); - this.setupAudio(this.currentSession); - } catch (error) { - this.updateCallStatus("failed", `鍛煎彨澶辫触22: ${error.message}`); - reject(error); + handleIncomingCall(session) { + if (session.direction === "incoming") { + console.log("鏉ョ數:", session.remote_identity.uri.toString()); + // 鍙互鍦ㄨ繖閲岃Е鍙� UI 閫氱煡 + if (this.onIncomingCall) { + this.onIncomingCall(session); } - }); + } } - setupPeerConnection(session) { - session.on("peerconnection", (pc) => { - const originalCreateOffer = pc.createOffer.bind(pc); + // 鏇存柊鐘舵�佸苟閫氱煡UI + updateStatus(type, text) { + console.log(`SIP鐘舵�佹洿鏂�: ${type} - ${text}`); + if (this.onStatusChange) { + this.onStatusChange({ type, text }); + } + } - pc.createOffer = async (offerOptions) => { - try { - const offer = await originalCreateOffer(offerOptions); - return this.normalizeSDP(offer); - } catch (error) { - console.error("鍒涘缓Offer澶辫触:", error); - throw error; - } + // 涓�閿嫧鍙� - 澧炲姞娉ㄥ唽鐘舵�佹鏌� + 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: <sip:@192.168.100.6>", + "Accept: application/sdp", + "Supported: replaces, timer", + "Allow: INVITE, ACK, BYE, CANCEL, OPTIONS", + ], + eventHandlers: { + progress: (e) => { + this.updateCallStatus("calling", "鍛煎彨涓�..."); + }, + failed: (e) => { + this.updateCallStatus("ended", `鍛煎彨澶辫触: ${e.cause}`); + }, + ended: (e) => { + this.updateCallStatus("ended", "閫氳瘽缁撴潫"); + }, + confirmed: (e) => { + this.updateCallStatus("connected", "閫氳瘽宸叉帴閫�"); + }, + }, + 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) => { + this.updateCallStatus('calling', '鍛煎彨涓�...'); + 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.currentSession.on('failed', (e) => { + this.updateCallStatus('failed', `鍛煎彨澶辫触2: ${e}`); + }); + + this.currentSession.on('ended', () => { + this.updateCallStatus('ended', '閫氳瘽宸茬粨鏉�'); + }); + + this.currentSession.on('confirmed', () => { + this.updateCallStatus('connected', '閫氳瘽宸叉帴閫�'); + }); + this.setupAudio(this.currentSession); } - - normalizeSDP(offer) { - let sdp = offer.sdp; - - // 鏍囧噯鍖朣DP - sdp = sdp.replace(/c=IN IP4.*\r\n/, "c=IN IP4 0.0.0.0\r\n"); - sdp = sdp.replace( - /m=audio \d+.*\r\n/, - "m=audio 9 UDP/TLS/RTP/SAVPF 0 8\r\n" - ); - - // 纭繚鍖呭惈鍩烘湰缂栬В鐮佸櫒 - if (!sdp.includes("PCMU/8000")) sdp += "a=rtpmap:0 PCMU/8000\r\n"; - if (!sdp.includes("PCMA/8000")) sdp += "a=rtpmap:8 PCMA/8000\r\n"; - - // 娣诲姞蹇呰灞炴�� - sdp += "a=rtcp-mux\r\n"; - sdp += "a=sendrecv\r\n"; - - console.log("鏍囧噯鍖栧悗鐨凷DP:", sdp); - return new RTCSessionDescription({ - type: offer.type, - sdp: sdp, - }); - } - - handleCallFailure(e, reject) { - if (e.response?.status_code === 422) { - const serverMinSE = e.response.headers["Min-SE"]?.[0]?.raw || "鏈煡"; - console.error(`鏈嶅姟鍣ㄨ姹� Min-SE 鈮� ${serverMinSE}锛屽綋鍓嶈缃�: 120`); - } - console.error("鍛煎彨澶辫触璇︽儏:", { - cause: e.cause, - message: e.message, - response: e.response && { - status: e.response.status_code, - reason: e.response.reason_phrase, - }, - }); - - let errorMessage = "鍛煎彨澶辫触"; - switch (e.cause) { - case "Incompatible SDP": - errorMessage = "濯掍綋鍗忓晢澶辫触锛岃妫�鏌ョ紪瑙g爜鍣ㄩ厤缃�"; - break; - case "488": - case "606": - errorMessage = "瀵规柟璁惧涓嶆敮鎸佸綋鍓嶅獟浣撻厤缃�"; - break; - case "422": - errorMessage = "浼氳瘽鍙傛暟涓嶆弧瓒虫湇鍔″櫒瑕佹眰"; - break; - default: - errorMessage = `鍛煎彨澶辫触3: ${e.cause || e.message}`; - } - - this.updateCallStatus("failed55", errorMessage); - reject(new Error(errorMessage)); - } - setupAudio(session) { session.connection.addEventListener("addstream", (e) => { const audioElement = document.getElementById("remoteAudio"); @@ -217,35 +180,19 @@ } }); } - + // 鎸傛柇褰撳墠閫氳瘽 endCall() { - if (this.currentSession) { + if (this.currentSession) { this.currentSession.terminate(); - this.updateCallStatus("ended", "閫氳瘽宸茬粨鏉�"); + this.updateCallStatus('ended', '閫氳瘽宸茬粨鏉�'); this.currentSession = null; } } - - updateStatus(type, text) { - console.log(`SIP鐘舵�佹洿鏂�: ${type} - ${text}`); - if (this.onStatusChange) { - this.onStatusChange({ type, text }); - } - } - + // 鏂板鏂规硶锛氭洿鏂伴�氳瘽鐘舵�� updateCallStatus(type, text) { console.log(`閫氳瘽鐘舵�佹洿鏂�: ${type} - ${text}`); if (this.onCallStatusChange) { this.onCallStatusChange({ type, text }); - } - } - - handleIncomingCall(session) { - if (session.direction === "incoming") { - console.log("鏉ョ數:", session.remote_identity.uri.toString()); - if (this.onIncomingCall) { - this.onIncomingCall(session); - } } } } -- Gitblit v1.9.3