From 53f4f0add976a4b402dfc2ad8547056227156693 Mon Sep 17 00:00:00 2001 From: WXL <1785969728@qq.com> Date: 星期三, 06 八月 2025 17:49:53 +0800 Subject: [PATCH] 测试完成 --- src/utils/sipService-xs.js | 217 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 217 insertions(+), 0 deletions(-) diff --git a/src/utils/sipService-xs.js b/src/utils/sipService-xs.js new file mode 100644 index 0000000..924ac14 --- /dev/null +++ b/src/utils/sipService-xs.js @@ -0,0 +1,217 @@ +import JsSIP from "jssip"; + +class SipService { + constructor() { + this.ua = null; + this.currentSession = null; + this.onStatusChange = null; + this.onCallStatusChange = null; + this.onIncomingCall = null; + } + + 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: [{ urls: "stun:stun.l.google.com:19302" }], + register: true, + session_expires: 180, + sessionTimersExpires: 300, + extraHeaders: ["Min-SE: 120"], + register_expires: 300, + connection_recovery_min_interval: 2, + connection_recovery_max_interval: 30, + pcConfig: { + iceTransportPolicy: "all", + rtcpMuxPolicy: "require", + bundlePolicy: "max-bundle" + } + }); + + 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); + throw error; + } + } + + makeCall(targetNumber) { + return new Promise((resolve, reject) => { + try { + if (!this.ua) { + throw new Error("SIP瀹㈡埛绔湭鍒濆鍖�"); + } + + if (!this.ua.isRegistered()) { + throw new Error("SIP鏈敞鍐岋紝鏃犳硶鍛煎彨"); + } + + const options = { + sessionTimers: false, // 鏆傛椂绂佺敤浠ュ噺灏戝吋瀹规�ч棶棰� + extraHeaders: [ + "Min-SE: 120", + "Accept: application/sdp", + "Supported: outbound" + ], + 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}@9.208.5.18`, + options + ); + + this.setupPeerConnection(this.currentSession); + this.setupAudio(this.currentSession); + + } catch (error) { + this.updateCallStatus("failed", `鍛煎彨澶辫触: ${error.message}`); + reject(error); + } + }); + } + + setupPeerConnection(session) { + session.on("peerconnection", (pc) => { + const originalCreateOffer = pc.createOffer.bind(pc); + + pc.createOffer = async (offerOptions) => { + try { + const offer = await originalCreateOffer(offerOptions); + return this.normalizeSDP(offer); + } catch (error) { + console.error("鍒涘缓Offer澶辫触:", error); + throw error; + } + }; + }); + } + + normalizeSDP(offer) { + let sdp = offer.sdp; + + // 1. 鏍囧噯鍖栬繛鎺ヨ + sdp = sdp.replace(/c=IN IP4.*\r\n/, "c=IN IP4 0.0.0.0\r\n"); + + // 2. 鏍囧噯鍖栭煶棰戝獟浣撹 + sdp = sdp.replace(/m=audio \d+.*\r\n/, + "m=audio 9 UDP/TLS/RTP/SAVPF 0 8\r\n"); + + // 3. 纭繚鍖呭惈鍩烘湰缂栬В鐮佸櫒 + 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"; + } + + // 4. 娣诲姞蹇呰灞炴�� + 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) { + console.error("鍛煎彨澶辫触璇︽儏:", { + cause: e.cause, + message: e.message, + response: e.response && e.response.status_code + }); + + let errorMessage = "鍛煎彨澶辫触"; + switch(e.cause) { + case "Incompatible SDP": + errorMessage = "濯掍綋鍗忓晢澶辫触锛岃妫�鏌ョ紪瑙g爜鍣ㄩ厤缃�"; + break; + case "488": + case "606": + errorMessage = "瀵规柟璁惧涓嶆敮鎸佸綋鍓嶅獟浣撻厤缃�"; + break; + default: + errorMessage = `鍛煎彨澶辫触: ${e.cause || e.message}`; + } + + this.updateCallStatus("failed", errorMessage); + reject(new Error(errorMessage)); + } + + 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.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); + } + } + } +} + +export default new SipService(); -- Gitblit v1.9.3