WebRTC与SIP协议转换网关:声网SDK与FreeSWITCH集成实战
1. 项目缘起与整体架构拆解WebRTC 和 SIP 这两个协议栈一个扎根在浏览器生态一个统治着传统电话网络各自都活得挺好但偏偏在很多企业通信场景里业务方就是想让浏览器里的用户直接拨通电话网络里的号码或者反过来让电话网络里的呼叫能落到网页端接听。这个需求听起来简单做起来却要跨越两套完全不同的信令体系、媒体传输机制和编解码协商逻辑。我接手这个项目的时候团队已经尝试过几种方案要么延迟高得离谱要么在 NAT 环境下频繁掉线最后决定走一条更可控的路线用声网的音视频 SDK 处理 WebRTC 侧的媒体采集、编码和传输用 FreeSWITCH 作为 SIP 侧的软交换核心中间自己写一个 Gateway 来做协议转换和媒体桥接。这个 Gateway 的核心任务可以拆成三块信令转换、媒体协商、以及媒体流的转发或转码。信令转换负责把 WebRTC 的 SDP 和 SIP 的 SDP 互相翻译同时把信令通道从 WebSocket 映射到 SIP over UDP/TCP媒体协商要处理 ICE、DTLS、SRTP 这些 WebRTC 特有的安全传输机制与 SIP 侧 RTP/RTCP 的对接媒体转发则是决定是走旁路还是走网关中转这直接影响到延迟和服务器带宽成本。我选择声网 SDK 的原因在于它已经把 WebRTC 的采集、前处理、编码、以及弱网对抗都封装得很成熟尤其是它的 SDK 内部已经处理了 ICE 和 DTLS 的很多细节我只需要关注怎么把它的媒体流引出来而不是从头去啃 WebRTC 原生 API 那一堆让人头大的回调。FreeSWITCH 这边它的 mod_sofia 模块本身就是个非常成熟的 SIP 栈支持大量的编解码和协议扩展而且它的 event socket 机制让我可以用外部进程来控制呼叫路由不需要写 C 模块。这样一来整个 Gateway 可以用 Python 或者 Node.js 来写业务逻辑通过 ESL 或者 mod_xml_curl 跟 FreeSWITCH 交互开发效率高很多。整体架构上我把它分成三个平面控制平面负责信令转换和呼叫路由媒体平面负责 RTP 流的转发或转码管理平面负责监控和日志。控制平面用 WebSocket 服务器接收来自浏览器的信令然后转换成 SIP 消息发给 FreeSWITCH媒体平面则根据协商结果决定是让声网 SDK 直接把 RTP 推给 FreeSWITCH还是通过一个 RTP 代理来中转。这个架构最大的好处是解耦。声网 SDK 负责它最擅长的事——在复杂网络环境下保证媒体质量FreeSWITCH 负责它最擅长的事——SIP 呼叫控制和 PSTN 对接中间的 Gateway 只做协议适配和状态同步逻辑清晰排查问题也容易定位。当然代价是引入了一个额外的跳数延迟会比纯端到端方案高一些但实测下来在可控范围内后面我会详细说延迟的构成和优化手段。2. 核心协议差异与信令转换细节2.1 WebRTC 与 SIP 在 SDP 上的关键差异WebRTC 的 SDP 和 SIP 的 SDP 虽然都叫 SDP但里面的内容差异大到让人怀疑它们是不是同一个标准。WebRTC 的 offer 里通常包含多个 m-line每个 m-line 下面挂着 ICE 候选、DTLS 指纹、SRTP 的加密套件还有 rtcp-mux 这种复用选项。而 SIP 侧的 SDP 往往更传统m-line 里就是简单的 RTP/AVP 描述编解码列表里是 PCMU、PCMA、G.722 这些安全机制要么没有要么用 SDES 来交换密钥。我第一次把两边 SDP 打出来对比的时候发现 WebRTC 的 SDP 行数几乎是 SIP 侧的三倍而且很多属性在 SIP 侧根本不认识。转换的时候最关键的是要把 WebRTC 的 ICE 候选和 DTLS 指纹剥离掉因为 FreeSWITCH 作为 SIP 侧不会去处理这些。同时要把 WebRTC 的 SRTP 加密信息转换成 FreeSWITCH 能理解的 SDES 或者直接走明文 RTP。这里有个坑如果直接走明文 RTP在公网上传输会有安全风险所以我在 Gateway 内部做了一次终结也就是 WebRTC 侧到 Gateway 是 SRTPGateway 到 FreeSWITCH 是明文 RTP但这段走内网或者专线风险可控。另一个坑是编解码协商WebRTC 默认优先用 Opus但 FreeSWITCH 默认可能只支持 PCMU/PCMA所以我在 Gateway 里强制把 Opus 转成 PCMU或者让 FreeSWITCH 加载 mod_opus 来支持 Opus 直通。2.2 信令通道的映射与状态同步WebRTC 侧的信令通常走 WebSocket消息格式可能是 JSON 或者自定义的文本协议SIP 侧则是标准的 SIP 消息走 UDP 或者 TCP。Gateway 要做的就是把 WebSocket 收到的 JSON 消息解析出来提取出 SDP、呼叫方向、目标号码等信息然后构造一个 SIP INVITE 发给 FreeSWITCH。这里的状态同步是个难点WebRTC 侧的呼叫状态和 SIP 侧的呼叫状态不是一一对应的比如 WebRTC 的 ICE 连接状态变化在 SIP 侧没有对应的信令但如果不处理可能会导致媒体流已经通了但 SIP 侧还在振铃。我的做法是在 Gateway 里维护一个状态机把 WebRTC 的 PeerConnection 状态和 SIP 的 dialog 状态做映射只有两边都进入 confirmed 状态才认为呼叫建立成功。还有一个细节是 DTMF 的传输。WebRTC 侧通常用 RFC 2833 或者 SIP INFO 来传 DTMF而 FreeSWITCH 支持 RFC 2833 和 SIP INFO但两者对 payload type 的协商可能不一致。我在 Gateway 里统一用 RFC 2833并且在 SDP 里显式声明 telephone-event 的 payload type 为 101这样两边都能识别。实测下来如果不显式声明FreeSWITCH 有时会忽略 DTMF导致用户按了数字但 IVR 没反应。2.3 呼叫路由与号码变换呼叫路由这块WebRTC 侧的用户可能拨的是一个内部短号而 SIP 侧需要的是一个完整的 E.164 号码。Gateway 里我加了一个路由表根据被叫号码的前缀来决定是路由到 FreeSWITCH 的某个 gateway 还是直接落到某个 IVR。路由表用 JSON 配置支持正则匹配和号码替换。比如用户拨 1001Gateway 把它替换成 861001 再发给 FreeSWITCHFreeSWITCH 再根据它的 dialplan 决定是振铃分机还是走外线。这个设计让号码变换逻辑集中在 Gateway 里FreeSWITCH 的 dialplan 可以保持简洁维护起来方便很多。3. 媒体面处理与声网 SDK 的集成要点3.1 声网 SDK 的媒体流引出方式声网 SDK 本身是一个端到端的解决方案它默认会把媒体流推到声网的云上然后由声网的云来转发。但在这个项目里我需要把媒体流引到本地推给 FreeSWITCH。声网 SDK 提供了几种方式来实现这个需求一种是使用它的旁路推流功能把流推到指定的 RTMP 或 RTSP 地址另一种是使用它的裸数据接口直接拿到 PCM 音频和 YUV 视频数据然后自己编码成 RTP 包发给 FreeSWITCH。我选择了后者因为旁路推流会引入额外的延迟而且格式转换不够灵活。裸数据接口的使用需要注意几个点音频数据是 PCM 格式采样率通常是 48kHz声道数可能是单声道或双声道需要重采样到 8kHz 或 16kHz 再编码成 PCMU 或 Opus视频数据是 YUV420p需要编码成 VP8 或 H.264然后打包成 RTP。声网 SDK 的回调频率是固定的音频每 10ms 或 20ms 回调一次视频每帧回调一次所以我在 Gateway 里起了一个发送线程按照 RTP 的时间戳递增来发包保证媒体流的连续性。这里有个坑如果发送线程被阻塞声网 SDK 的回调会堆积导致内存暴涨所以我在回调里只做数据拷贝把编码和发送放到另一个线程里做。3.2 RTP 打包与时间戳同步RTP 打包看起来简单但时间戳同步是个精细活。音频的 RTP 时间戳增量是采样率乘以打包间隔比如 8kHz 采样、20ms 打包增量就是 160。视频的 RTP 时间戳增量是 90kHz 除以帧率比如 30fps增量就是 3000。但声网 SDK 回调的时间戳是它内部的时钟跟系统时钟可能有偏差所以我用了一个平滑算法以第一个包的时间戳为基准后续包的时间戳按照理论增量累加如果实际回调时间跟理论时间偏差超过阈值就微调基准避免时间戳跳变导致播放端卡顿。实测下来这个平滑算法能把音频的抖动控制在 5ms 以内视频的抖动控制在 10ms 以内基本听不出问题。3.3 媒体中转与旁路的选择媒体中转还是旁路这是个架构层面的决策。中转的意思是所有 RTP 包都经过 GatewayGateway 可以做转码、录制、混音等操作但带宽成本高延迟也高旁路的意思是 Gateway 只做信令媒体流直接在 WebRTC 端和 FreeSWITCH 之间传输延迟低但 Gateway 无法干预媒体。我最终选择了中转原因有三个一是 WebRTC 端在 NAT 后面FreeSWITCH 不一定能直接连到它二是需要做 Opus 到 PCMU 的转码三是需要录制通话内容用于质检。中转带来的延迟增加大约在 20ms 到 40ms 之间对于语音通话来说完全可以接受。4. 实操部署与关键配置解析4.1 FreeSWITCH 侧的必要配置FreeSWITCH 的配置主要集中在 sofia profile 和 dialplan 上。首先要在 sofia profile 里开启 WebSocket 支持虽然这个项目里 WebSocket 是由 Gateway 处理的但 FreeSWITCH 需要能接受来自 Gateway 的 SIP 消息。我在 internal profile 里加了 Gateway 的 IP 到 ACL 白名单避免被当成攻击流量拦截。然后要确保 mod_opus 被加载如果要做 Opus 直通的话如果走转码则确保 mod_pcmu 和 mod_pcma 可用。dialplan 里我加了一条规则匹配 Gateway 发来的 INVITE然后根据被叫号码前缀路由到不同的 destination。extension namefrom_gateway condition fieldnetwork_addr expression^192\.168\.1\.100$/ condition fielddestination_number expression^86(\d)$ action applicationbridge datauser/$1/ /condition /extension这段 dialplan 的意思是如果 INVITE 来自 Gateway 的 IP并且被叫号码以 86 开头就把 86 去掉然后桥接到对应的分机。实际部署时IP 和号码规则要根据环境调整。4.2 Gateway 的信令处理流程Gateway 用 Node.js 写核心依赖是ws库处理 WebSocketsip库处理 SIP 消息。启动后它监听两个端口一个给 WebSocket一个给 SIP。WebSocket 收到浏览器的 offer 后解析出 SDP然后构造一个 SIP INVITE把 SDP 里的 ICE 和 DTLS 信息剥离替换成 FreeSWITCH 能接受的格式然后发给 FreeSWITCH。FreeSWITCH 返回 200 OK 后Gateway 把 SIP 的 answer SDP 转换成 WebRTC 的 answer SDP再通过 WebSocket 发回浏览器。整个流程里Gateway 要维护一个 session 对象记录呼叫 ID、SDP、状态等信息方便后续的 BYE 和 re-INVITE 处理。const sip require(sip); const WebSocket require(ws); const wss new WebSocket.Server({ port: 8080 }); const sipStack sip.create({ port: 5060 }); wss.on(connection, (ws) { ws.on(message, (message) { const msg JSON.parse(message); if (msg.type offer) { const sipInvite buildSipInvite(msg.sdp, msg.target); sipStack.send(sipInvite); } }); }); sipStack.on(invite, (req) { // 处理来自 FreeSWITCH 的 INVITE });这段代码是简化版实际还要处理认证、超时、错误响应等。我踩过的坑是SIP 消息的 Content-Length 必须准确否则 FreeSWITCH 会丢弃消息还有 Via 头的 branch 参数要唯一否则响应会匹配错。4.3 媒体面的 RTP 代理实现RTP 代理用 C 写因为要处理高并发的 UDP 包Node.js 的性能不够。代理监听两个端口一个收 WebRTC 侧的 SRTP一个收 FreeSWITCH 侧的 RTP。收到 SRTP 后先解密然后重新打包成 RTP 发给 FreeSWITCH收到 RTP 后加密成 SRTP 发给 WebRTC 侧。加解密用 libsrtp 库性能很好单核能处理上千路并发。代理还要处理 RTCP把发送端报告和接收端报告互相转换保证两边的统计信息一致。这里有个细节WebRTC 的 RTCP 里可能有 REMB 和 PLI这些在 SIP 侧没有对应需要丢弃或者转换成 FIR。5. 常见问题排查与性能优化实录5.1 单通与媒体超时问题单通是这类项目里最常见的问题表现是一方能听到另一方但反过来听不到。排查思路是先在 Gateway 上抓包看 RTP 包有没有双向流动。如果只有单向说明另一方的 RTP 包没到或者被丢弃了。常见原因有三个一是 SDP 里的 IP 地址是内网地址对方路由不到二是防火墙拦截了 UDP 包三是 SRTP 解密失败包被丢弃。我遇到过一次是 FreeSWITCH 的 SDP 里带了artcp-mux但 WebRTC 侧没开导致 RTCP 包发到了错误的端口。解决办法是在 Gateway 里强制两边都开或都关 rtcp-mux保持一致。5.2 音频卡顿与抖动缓冲音频卡顿通常跟抖动缓冲有关。WebRTC 侧有自适应抖动缓冲但 SIP 侧往往没有所以如果网络抖动大SIP 侧就会卡。我在 Gateway 里加了一个简单的抖动缓冲收到 RTP 包后不立即转发而是等 20ms 再转发这样能把抖动平滑掉。代价是增加了 20ms 延迟但换来了更稳定的音频。实测下来在网络抖动 50ms 以内的情况下加了抖动缓冲后卡顿率从 5% 降到了 0.5% 以下。5.3 呼叫建立延迟优化呼叫建立延迟主要来自信令往返和 ICE 协商。WebRTC 的 ICE 协商通常需要收集候选、连通性检查这个过程可能耗时几百毫秒。我的优化手段是在 Gateway 里预收集 FreeSWITCH 侧的候选提前告诉 WebRTC 侧减少连通性检查的时间同时把 SIP 的 100 Trying 和 180 Ringing 及时转发给 WebRTC 侧让用户尽早听到回铃音。经过优化呼叫建立延迟从平均 1.2 秒降到了 600ms 左右。问题现象可能原因排查手段解决方案单通SDP IP 错误抓包看 RTP 方向修正 SDP 里的 IP音频卡顿抖动缓冲不足统计 RTP 丢包和抖动增加抖动缓冲呼叫建立慢ICE 协商耗时看 ICE 候选收集时间预收集候选DTMF 无效payload type 不一致看 SDP 里的 telephone-event统一为 101视频花屏丢包或时间戳跳变看 RTP 序列号和时间戳加平滑算法5.4 并发压力下的资源管理并发量上来之后资源管理变得很重要。每个呼叫会占用一个 session 对象、一个 RTP 代理端口、以及一定的 CPU 用于加解密和转码。我在 Gateway 里加了一个连接池复用 RTP 代理端口减少端口耗尽的风险同时用 Redis 存储 session 状态方便水平扩展。实测单台 4 核 8G 的服务器能稳定支撑 500 路并发通话CPU 占用在 60% 左右瓶颈主要在 SRTP 加解密上如果换成支持 AES-NI 的 CPU能提升到 800 路以上。6. 个人实操体会与后续扩展方向这个项目从立项到稳定运行花了大概三个月中间踩的坑主要集中在 SDP 转换和媒体面同步上。我最大的体会是不要试图让 WebRTC 和 SIP 完全对等它们的设计哲学不一样强行对等只会让代码越来越复杂。更好的做法是在 Gateway 里做一次终结把两边的差异消化掉让每一侧都觉得自己在跟同类通信。另一个体会是日志要打全尤其是 SDP 和 RTP 的统计信息出问题的时候这些日志能省下大量排查时间。后续如果要扩展我觉得有几个方向值得尝试一是支持视频通话现在只做了音频视频的 RTP 打包和关键帧请求会更复杂二是支持会议混音把多路 WebRTC 流混成一路发给 SIP 侧三是把 Gateway 容器化用 Kubernetes 做弹性伸缩应对突发流量。这些方向我还在摸索等有成熟经验了再另开一篇细说。

相关新闻

CuteLogger 集成指南:用 Qt 构建可靠日志系统的核心思路与避坑

CuteLogger 集成指南:用 Qt 构建可靠日志系统的核心思路与避坑

简介:CuteLogger 是一套面向 Qt 开发者的轻量级日志库,用于替代 qDebug 等原生输出,解决多线程工程中日志信息不全、格式不统一、扩展性不足等问题。它几乎能记录文件名、源代码行与函数签名,并通过附加程序机制灵活输出到文件、控…

2026/10/15 8:27:39 阅读更多 →
Nginx高可用集群实战:Keepalived+虚拟IP实现主备自动切换

Nginx高可用集群实战:Keepalived+虚拟IP实现主备自动切换

1. 为什么要搭建Nginx高可用集群1.1 单点故障的现实困境很多人刚开始接触Nginx时,都是在单台服务器上部署,做反向代理、静态资源托管、负载均衡转发。这套玩法在开发环境和中小流量场景下没什么问题,但当它真正扛起生产流量时,一个…

2026/10/15 8:27:39 阅读更多 →
artcraft 项目实战:从创意到成品的流程编排与参数体系设计

artcraft 项目实战:从创意到成品的流程编排与参数体系设计

1. 从“artcraft”这个名字说起:它到底想解决什么问题第一次看到“artcraft”这个项目标题,我的直觉是:这大概率是一个把“艺术创作”和“手工实现”捏合在一起的工具型项目。拆开来看,“art”指向视觉表达、创意生成、审美输出&a…

2026/10/15 8:26:36 阅读更多 →

最新新闻

2026东莞离婚纠纷维权优选:广东卡夫律所本土裁判优势深度解析

2026东莞离婚纠纷维权优选:广东卡夫律所本土裁判优势深度解析

2026东莞离婚纠纷维权优选:广东卡夫律所本土裁判优势深度解析在婚姻家事纠纷处理中,不同于普通民商事案件的刚性法条适用,离婚诉讼更看重本地法院裁判尺度、家事审判惯例、法官自由裁量标准。2026年东莞家事司法审判日趋精细化,抚…

2026/10/15 9:47:44 阅读更多 →
PyTorch实战Mask R-CNN实例分割:从数据准备到工业部署

PyTorch实战Mask R-CNN实例分割:从数据准备到工业部署

1. 项目概述:这不是“又一个目标检测demo”,而是一次真正落地的像素级理解实践Mask R-CNN不是PyTorch官方模型库里的一个可选模块,它是一套完整的方法论——把“图像里有什么”(分类)、“东西在哪儿”(定位…

2026/10/15 9:47:44 阅读更多 →
大文件秒传技术:JSP实现原理与优化方案

大文件秒传技术:JSP实现原理与优化方案

1. 大文件上传的技术挑战与秒传原理在Web开发领域,文件上传功能看似基础实则暗藏玄机。当文件体积超过100MB时,传统的表单上传方式就会暴露出各种问题:网络波动导致上传失败、服务器内存溢出、用户等待时间过长等。更糟糕的是,当用…

2026/10/15 9:47:44 阅读更多 →
IQ测试题含答案:30道题的结构设计与判分逻辑

IQ测试题含答案:30道题的结构设计与判分逻辑

简介:这是一份面向求职面试、企业招聘及自我测评场景的IQ测试训练资料,涵盖言语理解、图形推理、数据分析等常见题型,共约30道经典题目,并配有详细答案与解析思路。资源包为单个docx文档,体积4.26MB,便于阅…

2026/10/15 9:47:44 阅读更多 →
OSPF协议详解与华为实验配置指南

OSPF协议详解与华为实验配置指南

1. OSPF协议基础认知作为网络工程师日常接触最广泛的IGP协议之一,OSPF(Open Shortest Path First)以其链路状态特性和分层设计理念,成为企业网和运营商网络的首选路由协议。与距离矢量协议不同,OSPF路由器通过洪泛LSA&…

2026/10/15 9:47:44 阅读更多 →
RTD2718Q驱动芯片4K144显示方案:规格书解读与调试实战

RTD2718Q驱动芯片4K144显示方案:规格书解读与调试实战

简介:这份资源是Realtek RTD2718Q-VD-CG显示驱动芯片的官方规格书,面向显示器方案工程师、嵌入式开发人员及硬件选型人员,用于了解该芯片在4K144Hz高刷显示场景下的接口能力与系统集成方式。芯片支持HDMI 2.1与DP 1.4双路输入,兼容…

2026/10/15 9:46:43 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →