| | |
| | | } |
| | | // 获取医院配置方法 |
| | | getHospitalConfig() { |
| | | const orgName=localStorage.getItem("orgname"); |
| | | const orgName = localStorage.getItem("orgname"); |
| | | return HOSPITAL_CONFIG[orgName] || HOSPITAL_CONFIG.default; |
| | | } |
| | | init(baseConfig) { |
| | |
| | | |
| | | // 根据机构名称获取对应的服务器配置 |
| | | const hospitalConfig = this.getHospitalConfig(orgName); |
| | | console.log(hospitalConfig,'88'); |
| | | console.log(hospitalConfig, "88"); |
| | | |
| | | // 合并配置 |
| | | this.currentConfig = { |
| | |
| | | ); |
| | | |
| | | this.updateStatus("connecting", "连接中..."); |
| | | console.log(baseConfig.sipUri,'baseConfig.sipUri'); |
| | | console.log(baseConfig.sipUri, "baseConfig.sipUri"); |
| | | |
| | | this.ua = new JsSIP.UA({ |
| | | sockets: [new JsSIP.WebSocketInterface(this.currentConfig.wsUrl)], |
| | |
| | | }); |
| | | } |
| | | |
| | | // normalizeSDP(offer) { |
| | | // let sdp = offer.sdp; |
| | | // console.log("原始SDP:", sdp); // 调试用,捕获原始SDP |
| | | // // 标准化SDP |
| | | // 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("标准化后的SDP:", sdp); |
| | | // return new RTCSessionDescription({ |
| | | // type: offer.type, |
| | | // sdp: sdp, |
| | | // }); |
| | | // } |
| | | // 在 SipService 类中新增方法,用于获取针对特定服务器的SDP处理策略 |
| | | getSDPNormalizationStrategy(orgName) { |
| | | const strategies = { |
| | | 龙泉市人民医院: "conservative", // 保守策略:最小化修改,优先兼容 |
| | | 丽水市中医院: "aggressive", // 激进策略:保持原有强标准化逻辑 |
| | | // 可以为其他机构添加更多策略 |
| | | }; |
| | | return strategies[orgName] || "moderate"; // 默认策略 |
| | | } |
| | | |
| | | /** |
| | | * 标准化SDP Offer - 修复龙泉市人民医院488错误 |
| | | * 核心思路:从“强制覆盖”改为“智能修补”,针对不同服务器使用差异化策略 |
| | | */ |
| | | normalizeSDP(offer) { |
| | | const orgName = localStorage.getItem("orgname"); |
| | | const strategy = this.getSDPNormalizationStrategy(orgName); |
| | | let sdp = offer.sdp; |
| | | |
| | | // 标准化SDP |
| | | 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" |
| | | ); |
| | | console.log(`[SDP标准化] 机构: ${orgName}, 策略: ${strategy}`); |
| | | console.log("[SDP标准化] 原始SDP:", sdp); |
| | | |
| | | // 确保包含基本编解码器 |
| | | 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"; |
| | | if (strategy === "conservative") { |
| | | // ==================== 保守策略:针对龙泉市人民医院等严格服务器 ==================== |
| | | // 原则:除非必要,否则不修改原有SDP结构,仅添加缺失的关键属性 |
| | | |
| | | // 添加必要属性 |
| | | sdp += "a=rtcp-mux\r\n"; |
| | | sdp += "a=sendrecv\r\n"; |
| | | // 1. 谨慎处理连接地址:仅在地址是明显内网地址时才修改 |
| | | const privateIPRegex = |
| | | /c=IN IP4 (192\.168|10\.|172\.(1[6-9]|2[0-9]|3[0-1]))/; |
| | | if (privateIPRegex.test(sdp)) { |
| | | sdp = sdp.replace(/c=IN IP4.*\r\n/, "c=IN IP4 0.0.0.0\r\n"); |
| | | console.log("[SDP标准化] 已修改连接地址为 0.0.0.0"); |
| | | } |
| | | |
| | | console.log("标准化后的SDP:", sdp); |
| | | // 2. 保持媒体行原样,不强制修改端口和协议 |
| | | // sdp = sdp.replace(/m=audio \d+.*\r\n/, "m=audio 9 UDP/TLS/RTP/SAVPF 0 8\r\n"); |
| | | |
| | | // 3. 智能添加基础编解码器映射(仅在缺失时添加) |
| | | const codecMappings = [ |
| | | { pt: 0, name: "PCMU/8000" }, |
| | | { pt: 8, name: "PCMA/8000" }, |
| | | ]; |
| | | |
| | | codecMappings.forEach((codec) => { |
| | | const rtpmapPattern = `a=rtpmap:${codec.pt} ${codec.name}`; |
| | | const payloadPattern = ` ${codec.pt} `; |
| | | |
| | | // 只有当SDP中包含该负载类型但缺少详细映射时才添加 |
| | | if (sdp.includes(payloadPattern) && !sdp.includes(rtpmapPattern)) { |
| | | sdp += `${rtpmapPattern}\r\n`; |
| | | console.log(`[SDP标准化] 已添加编解码器映射: ${rtpmapPattern}`); |
| | | } |
| | | }); |
| | | |
| | | // 4. 条件性添加必要属性(避免重复) |
| | | const essentialAttributes = [ |
| | | { attr: "a=rtcp-mux", desc: "RTCP复用" }, |
| | | { attr: "a=sendrecv", desc: "双向媒体流" }, |
| | | ]; |
| | | |
| | | essentialAttributes.forEach((item) => { |
| | | if (!sdp.includes(item.attr)) { |
| | | sdp += `${item.attr}\r\n`; |
| | | console.log(`[SDP标准化] 已添加属性: ${item.desc}`); |
| | | } |
| | | }); |
| | | } else if (strategy === "aggressive") { |
| | | // ==================== 激进策略:保持原有逻辑 ==================== |
| | | 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"; |
| | | } else { |
| | | // ==================== 默认策略:平衡方案 ==================== |
| | | // 适度修改,兼顾兼容性和功能性 |
| | | sdp = sdp.replace(/c=IN IP4.*\r\n/, "c=IN IP4 0.0.0.0\r\n"); |
| | | |
| | | // 仅在媒体行格式明显异常时修改 |
| | | if (!sdp.match(/m=audio \d+ RTP\/AVP/)) { |
| | | sdp = sdp.replace( |
| | | /m=audio \d+.*\r\n/, |
| | | "m=audio 9 UDP/TLS/RTP/SAVPF 0 8\r\n" |
| | | ); |
| | | } |
| | | |
| | | // 智能添加缺失的属性 |
| | | if (!sdp.includes("a=rtcp-mux")) sdp += "a=rtcp-mux\r\n"; |
| | | if (!sdp.includes("a=sendrecv")) sdp += "a=sendrecv\r\n"; |
| | | } |
| | | |
| | | console.log("[SDP标准化] 标准化后SDP:", sdp); |
| | | return new RTCSessionDescription({ |
| | | type: offer.type, |
| | | sdp: sdp, |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * 增强的SDP调试方法 - 用于对比分析 |
| | | */ |
| | | debugSDPComparison(originalOffer, normalizedOffer, context) { |
| | | console.group(`[SDP调试] ${context}`); |
| | | console.log( |
| | | "原始SDP媒体行:", |
| | | originalOffer.sdp.match(/m=audio.*\r\n/)?.[0] || "未找到" |
| | | ); |
| | | console.log( |
| | | "标准化后媒体行:", |
| | | normalizedOffer.sdp.match(/m=audio.*\r\n/)?.[0] || "未找到" |
| | | ); |
| | | console.log( |
| | | "原始编解码器列表:", |
| | | originalOffer.sdp.match(/a=rtpmap:\d+.*\r\n/g) || [] |
| | | ); |
| | | console.log( |
| | | "标准化后编解码器列表:", |
| | | normalizedOffer.sdp.match(/a=rtpmap:\d+.*\r\n/g) || [] |
| | | ); |
| | | console.groupEnd(); |
| | | } |
| | | |
| | | // 在 setupPeerConnection 方法中集成调试功能 |
| | | setupPeerConnection(session) { |
| | | session.on("peerconnection", (pc) => { |
| | | const originalCreateOffer = pc.createOffer.bind(pc); |
| | | |
| | | pc.createOffer = async (offerOptions) => { |
| | | try { |
| | | const offer = await originalCreateOffer(offerOptions); |
| | | const normalizedOffer = this.normalizeSDP(offer); |
| | | |
| | | // 调试信息输出 |
| | | this.debugSDPComparison(offer, normalizedOffer, "Offer创建阶段"); |
| | | |
| | | return normalizedOffer; |
| | | } catch (error) { |
| | | console.error("创建Offer失败:", error); |
| | | throw error; |
| | | } |
| | | }; |
| | | }); |
| | | } |
| | | handleCallFailure(e, reject) { |
| | | if (e.response?.status_code === 422) { |
| | | const serverMinSE = e.response.headers["Min-SE"]?.[0]?.raw || "未知"; |