From 209b2f7b9a0b2abc4043f22bed9b06ab2650ba59 Mon Sep 17 00:00:00 2001
From: WXL <1785969728@qq.com>
Date: 星期四, 07 八月 2025 10:03:31 +0800
Subject: [PATCH] 测试完成

---
 src/utils/sipService.js |  256 +++++++++++++++++++++++++++++++++++---------------
 1 files changed, 178 insertions(+), 78 deletions(-)

diff --git a/src/utils/sipService.js b/src/utils/sipService.js
index a8ab17c..57209b7 100644
--- a/src/utils/sipService.js
+++ b/src/utils/sipService.js
@@ -1,117 +1,217 @@
-import JsSIP from 'jssip'
+import JsSIP from "jssip";
 
 class SipService {
   constructor() {
-    this.ua = null
-    this.currentSession = null
-    this.onStatusChange = null // 鐘舵�佸彉鍖栧洖璋�
+    this.ua = null;
+    this.currentSession = null;
+    this.onStatusChange = null;
+    this.onCallStatusChange = null;
+    this.onIncomingCall = 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,
-        realm: config.realm,
+        iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
         register: true,
-        register_expires: 300, // 娉ㄥ唽鏈夋晥鏈�(绉�)
-        connection_recovery_min_interval: 2, // 鏈�灏忛噸杩為棿闅�
-        connection_recovery_max_interval: 30 // 鏈�澶ч噸杩為棿闅�
-      })
+        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.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)
-      })
+      // 浜嬩欢鐩戝惉
+      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)
+      this.updateStatus("failed", `鍒濆鍖栧け璐�: ${error.message}`);
+      console.error("SIP鍒濆鍖栧け璐�:", error);
+      throw error;
     }
   }
 
-  // 鏇存柊鐘舵�佸苟閫氱煡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瀹㈡埛绔湭鍒濆鍖�')
-    }
+    return new Promise((resolve, reject) => {
+      try {
+        if (!this.ua) {
+          throw new Error("SIP瀹㈡埛绔湭鍒濆鍖�");
+        }
 
-    if (!this.ua.isRegistered()) {
-      throw new Error('SIP鏈敞鍐岋紝鏃犳硶鍛煎彨')
-    }
+        if (!this.ua.isRegistered()) {
+          throw new Error("SIP鏈敞鍐岋紝鏃犳硶鍛煎彨");
+        }
 
-    const 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 }
-    }
+        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}@192.168.100.6`, options)
-    this.setupAudio(this.currentSession)
+        this.currentSession = this.ua.call(
+          `sip:${targetNumber}@192.168.10.124`,
+          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.currentSession = null
+      this.currentSession.terminate();
+      this.updateCallStatus("ended", "閫氳瘽宸茬粨鏉�");
+      this.currentSession = null;
     }
   }
 
-  // 澶勭悊闊抽娴�
-  setupAudio(session) {
-    session.connection.addEventListener('addstream', (e) => {
-      const audioElement = document.getElementById('remoteAudio')
-      if (audioElement) {
-        audioElement.srcObject = e.stream
-      }
-    })
+  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())
-      // 杩欓噷鍙互瑙﹀彂UI閫氱煡
+    if (session.direction === "incoming") {
+      console.log("鏉ョ數:", session.remote_identity.uri.toString());
+      if (this.onIncomingCall) {
+        this.onIncomingCall(session);
+      }
     }
   }
 }
 
-export default new SipService()
+export default new SipService();

--
Gitblit v1.9.3