RTSP播放器开发实战:从拉流到出画的工程避坑指南
简介EasyPlayer-RTSP-Win 是一套面向安防流媒体开发者的 Windows RTSP 播放器完整源码工程适合需要在商业项目中集成稳定播放组件的 C/C# 工程师。它支持 H.264、H.265、MPEG4、MJPEG 视频编码与 G711A、G711U、G726、AAC 音频编码兼容 RTSP over TCP/UDP 两种传输模式并具备软硬解码、直播、录像、抓图等能力。相比基于 ffmpeg 的通用播放器该项目针对各厂家 RTSP 自定义字段做了大量兼容处理这正是实际项目中最易踩坑之处。压缩包共 688 个文件、约 55.63MB以 304 个 h 头文件、52 个 cpp 源文件、30 个 lib 库、94 个 dll 动态库为主体另含 ocx 控件、ActiveX 注册脚本、多套 vcxproj/sln 工程与皮肤位图资源目录结构完整。已有 902 人学习下载可据此研究 RTSP 协议兼容、解码渲染与 OCX 封装思路。1. 一个 RTSP 播放器到底难在哪从拉流到出画的那几秒很多人第一次接触 RTSP 播放器直觉是「不就是把一路流拉下来解码显示吗」。真上手才发现从发出 DESCRIBE 到屏幕上出现第一帧中间要跨过 SDP 解析、RTP 收包、乱序重排、时间戳对齐、解码器初始化、音视频同步这一整条链路任何一环没处理好表现就是黑屏、花屏、音画不同步或者跑几分钟就卡死。EasyPlayer-RTSP-Win 这个方向之所以值得单独拿出来讲是因为它把「精炼、高效」当成目标视频覆盖 H.264/H.265音频覆盖 G.711/G.726/AAC传输同时支持 RTP over UDP 和 RTP over TCP 两种模式。这几个词看着平平无奇实际每一个都对应一类真实工程取舍。这篇文章面向的是要在 Windows 上做实时预览、做安防接入、做低延迟监看的开发者我会把选型理由、关键参数、可复现的步骤和踩过的坑一条条摊开讲让你看完能自己搭一个最小可用的 RTSP 播放链路而不是停在「知道有这么个东西」。2. 先把 RTSP 会话和两种传输模式讲透UDP 与 TCP 到底怎么选2.1 RTSP 会话的完整交互顺序RTSP 本质是一个「控制协议」它自己不搬媒体数据只负责协商和指挥。一次完整的播放会话客户端和服务端之间大致走这几步先 OPTIONS 问对方支持哪些方法再 DESCRIBE 拿到 SDP 描述SDP 里写明了有几路媒体、每路用什么编码、payload type 是多少、控制 URL 是什么接着 SETUP 为每一路媒体建立传输通道这一步会确定用 UDP 还是 TCP、端口号是多少然后 PLAY 真正开始推流结束时 TEARDOWN 收尾。很多人调试时只盯着 PLAY 之后有没有画面其实大量问题出在 SETUP 阶段——传输模式没协商对后面怎么等都不会有数据。SDP 里几个字段必须看懂mvideo 0 RTP/AVP 96表示这是一路视频payload type 是 96动态类型具体编码要看artpmapartpmap:96 H264/90000说明 96 对应 H.264时钟频率 90000afmtp:96 packetization-mode1里的 packetization-mode 决定了 H.264 的打包方式mode 1 表示支持 FU-A 分片这是绝大多数场景的默认值。H.265 在 SDP 里通常写成H265/90000payload type 也走动态分配。音频侧 G.711 一般是静态 payload typePCMU 是 0PCMA 是 8AAC 则多为动态类型artpmap:97 MPEG4-GENERIC/44100/2这种写法要能认出来。2.2 RTP over UDP 与 RTP over TCP 的取舍这是整个播放器里最需要想清楚的一个选择没有绝对优劣只有场景匹配。UDP 模式下RTP 包直接走 UDP 传输延迟最低服务端发多少客户端收多少不额外引入重传等待。代价是丢包不可恢复弱网下会花屏、卡顿而且很多企业内网和云主机默认封 UDP 高端口SETUP 阶段就可能失败。TCP 模式下RTP 包被封装进 RTSP 的 interleaved 通道通过同一条 TCP 连接传输格式是$ 通道号 长度 RTP 数据。它的好处是穿透性好、不丢包坏处是 TCP 的重传和拥塞控制会引入延迟抖动一旦网络拥塞延迟会累积实时性反而变差。我的经验判断很直接局域网、专网、延迟敏感比如云台控制配合监看优先 UDP跨公网、NAT 环境复杂、对完整性要求高于延迟的场景用 TCP。EasyPlayer 这类播放器同时支持两种模式本质就是把选择权交给部署环境而不是替用户拍板。2.3 用 ffmpeg 快速验证一路流能不能拉通在写播放器之前先用现成工具确认流本身没问题能省掉一半排查时间。下面这条命令只做拉流不做转码用来判断服务端和网络是否正常# 强制用 TCP 传输拉一路 RTSP只解码不保存观察是否有报错 ffplay -rtsp_transport tcp -i rtsp://user:pass192.168.1.10:554/stream1 # 强制用 UDP 传输对比延迟和丢包表现 ffplay -rtsp_transport udp -i rtsp://user:pass192.168.1.10:554/stream1 # 只看流信息不播放确认编码格式和分辨率 ffprobe -rtsp_transport tcp -show_streams rtsp://user:pass192.168.1.10:554/stream1-rtsp_transport这个参数就是用来强制传输模式的默认 ffmpeg 会优先尝试 UDP。如果 UDP 报Connection timed out而 TCP 正常基本可以判定是网络策略封了 UDP。ffprobe输出的codec_name字段能直接告诉你这路流是 h264 还是 hevcsample_rate和channels告诉你音频参数这些信息在配置解码器时要用到。注意这里只是验证手段真正做播放器时这些逻辑要自己实现不能依赖外部进程。3. 视频解码链路H.264 与 H.265 在 Windows 上怎么落地3.1 从 RTP 包还原出一帧完整图像RTP 收上来的不是完整帧而是被切碎的 NAL 单元。H.264 常见的打包方式有单 NAL 单元模式、STAP-A 聚合模式、FU-A 分片模式。FU-A 最典型一个大的 NAL 被拆成多个 RTP 包每个包头部有 FU indicator 和 FU headerstart 位标记分片开始end 位标记分片结束。收包时要按时间戳和序列号把这些分片拼回一个完整 NAL再交给解码器。H.265 的分片机制类似叫 FU但 NAL 头是两字节类型字段位置不同解析时不能照搬 H.264 的代码。这是很多人第一次同时支持两种编码时翻车的地方——用 H.264 的解析逻辑去处理 H.265结果就是解码器一直报错。下面是一段简化的 H.264 FU-A 重组逻辑用 Python 示意重点是判断逻辑而不是完整实现# 简化示意H.264 FU-A 分片重组 # rtp_payload 是去掉 RTP 头之后的负载 def reassemble_h264(rtp_payload, buffer): nal_type rtp_payload[0] 0x1F if nal_type 28: # FU-A fu_header rtp_payload[1] start (fu_header 7) 0x01 end (fu_header 6) 0x01 # 原始 NAL 类型藏在 FU indicator 低 5 位 original_type rtp_payload[0] 0xE0 | (fu_header 0x1F) if start: buffer bytearray([original_type]) # 重建 NAL 头 buffer.extend(rtp_payload[2:]) # 去掉 FU 头追加数据 if end: return bytes(buffer), True # 一帧完整 NAL return buffer, False else: # 单 NAL 或 STAP-A直接处理 return rtp_payload, True这段代码的关键在original_type的重建FU-A 把原始 NAL 头拆成了两半FU indicator 保留 F 和 NRI 位FU header 低 5 位才是真正的类型重组时必须把这两部分拼回去否则解码器拿到的 NAL 头是错的。buffer要按时间戳维护同一个时间戳的分片归到一组跨时间戳就要清空重来。实际工程里还要处理丢包——如果 start 收到了但 end 一直没来不能无限等要有超时丢弃机制否则内存会涨。3.2 解码器选型软解还是硬解Windows 上做 H.264/H.265 解码常见路线有三条纯软解比如 FFmpeg 的 libavcodec、DXVA2/D3D11VA 硬解、Media Foundation。软解兼容性最好什么流都能吃代价是 1080p 多路并发时 CPU 占用高硬解把解码丢给 GPUCPU 占用能降一大截但对流的规格有要求比如某些 profile 或分辨率不支持遇到不支持的流会直接失败需要回退到软解。我的建议是做成「硬解优先、失败回退软解」的策略。初始化硬解时先探测 GPU 能力创建解码器失败就切软解不要一上来就写死。H.265 的硬解支持比 H.264 窄老显卡可能只支持 H.264这点在部署前要确认目标机器的 GPU 型号。另外硬解输出的往往是 GPU 纹理要显示到窗口还得做一次纹理拷贝或映射这一步的延迟也要算进去。3.3 时间戳与音视频同步的基本做法RTP 包里的时间戳是 32 位、以时钟频率为单位递增的H.264/H.265 通常是 90000Hz。它会在回绕后归零所以不能直接拿来做差值要先做回绕处理。音视频同步的常见做法是选一路为主时钟一般用音频因为人对声音断续更敏感视频帧根据自身时间戳和主时钟的偏差决定是立即显示还是等待。偏差超过阈值就丢帧或重复帧阈值一般设在 40ms 到 100ms 之间太小会频繁抖动太大会明显不同步。这里有个容易忽略的点RTSP 的 RTP 时间戳和实际墙钟时间不是线性对应的服务端可能因为编码或网络原因产生抖动所以同步逻辑里通常还要做一层平滑比如用滑动平均估计时钟偏移而不是每帧都硬对齐。4. 音频解码与多格式兼容G.711、G.726、AAC 的差异处理4.1 三种音频格式的解码特点G.711 是最简单的分 PCMUμ-law和 PCMAA-law两种8kHz 采样、8 位量化解码就是查表或简单运算CPU 开销几乎可以忽略。它的 payload type 是静态的PCMU 是 0PCMA 是 8SDP 里一般不会写 rtpmap直接靠 payload type 就能判断。G.726 是 ADPCM有 16k/24k/32k/40k 几种速率解码需要维护自适应预测状态比 G.711 复杂一点但也不算重。AAC 最复杂尤其是带 ADTS 头和不带头的区别以及 SBR 参数的处理通常要依赖完整解码库。这三种格式在同一个播放器里共存意味着音频解码模块要能根据 SDP 动态选择解码器而不是编译期写死。常见做法是维护一张 payload type 到解码器的映射表SETUP 时根据 SDP 注册对应的解码器实例。4.2 音频重采样与播放缓冲解码出来的音频采样率不一定和声卡一致比如 G.711 是 8kHz而声卡通常跑 44.1kHz 或 48kHz中间必须做重采样。重采样质量直接影响听感简单的线性插值会有明显失真建议用带限插值。播放侧还要有一个缓冲队列用来吸收网络抖动缓冲太短会爆音太长会延迟。一般设 100ms 到 300ms 比较稳妥具体要看网络质量。下面是一个音频缓冲队列的简化结构用 Python 示意# 简化示意带抖动的音频播放缓冲 import collections class AudioBuffer: def __init__(self, target_ms200, sample_rate48000): self.queue collections.deque() self.target_frames int(target_ms * sample_rate / 1000) self.sample_rate sample_rate def push(self, pcm_frames): # 解码后的 PCM 帧入队 self.queue.extend(pcm_frames) def pull(self, need_frames): # 播放侧按需取数据不足则补静音避免爆音 if len(self.queue) need_frames: out list(self.queue) [0] * (need_frames - len(self.queue)) self.queue.clear() return out out [self.queue.popleft() for _ in range(need_frames)] return outtarget_ms控制缓冲深度网络差就调大代价是延迟增加。pull里补静音而不是直接返回空是为了避免播放设备因为数据不足产生爆音。实际工程里还要处理采样率转换这里省略了重采样部分只保留缓冲逻辑。4.3 音视频同步中音频侧的特殊处理音频一般作为主时钟因为它对连续性要求高。但音频解码和播放的节奏和视频不同视频可以丢帧音频丢帧会直接听出来断裂。所以同步策略上视频向音频对齐而不是反过来。当视频落后音频太多丢视频帧追赶当视频超前音频等待或重复帧。音频侧本身尽量不做丢帧只做缓冲调节。5. 避坑与排查那些让播放器「看起来能用其实不能用」的问题5.1 现象UDP 模式能连上但一直黑屏原因通常是 SETUP 协商的端口和实际发流端口不一致或者客户端没有正确解析 SDP 里的m行端口。有些服务端在 SDP 里写的端口是 0实际端口通过 SETUP 响应返回如果客户端只读 SDP 不读 SETUP 响应就会收不到数据。解决办法是严格以 SETUP 响应里的server_port和client_port为准不要相信 SDP 里的默认值。5.2 现象H.265 流播放几秒后解码器报错退出多半是 NAL 头解析用了 H.264 的逻辑。H.265 的 NAL 头是两字节类型在第一个字节的 1 到 6 位和 H.264 完全不同。排查方法是把收到的前几个 RTP 包负载打印出来对照 H.265 的 NAL 类型表确认解析是否正确。修复就是为 H.265 单独写一套分片重组逻辑不要复用 H.264 的。5.3 现象TCP 模式延迟越跑越大这是 TCP 重传累积的典型表现。网络一旦出现丢包TCP 会重传重传的数据到达时已经晚了但播放器如果还按原时间戳播放就会越积越多。解决办法是在 TCP 模式下引入主动丢帧策略当缓冲队列超过阈值直接丢弃最旧的数据追赶实时。这个策略在 UDP 下不需要因为 UDP 本来就不重传。5.4 现象G.711 音频有杂音或断续常见原因是重采样没做或者做得太粗糙8kHz 直接当 48kHz 播会变调线性插值会有混叠。另一个原因是缓冲太浅网络稍微抖动就断供。排查时先确认解码后的采样率再检查重采样算法最后调大缓冲看是否改善。如果调大缓冲后杂音消失就是缓冲问题如果还在就是重采样问题。5.5 现象多路并发时 CPU 飙满大概率是全部走了软解。排查方法是看解码器初始化日志确认硬解是否成功创建。如果硬解失败要看失败原因——是 GPU 不支持该编码还是驱动版本太旧。解决路径是升级驱动或换支持硬解的机器实在不行就限制并发路数或者降低分辨率。6. 进阶技巧把首帧时间和稳定性再压一压首帧时间从 PLAY 到第一帧显示是实时预览体验的关键指标能压到 500ms 以内就算不错。我一般会从三个地方下手。第一SDP 解析和 SETUP 并行化不要串行等DESCRIBE 拿到 SDP 后立刻对每一路发起 SETUP减少往返。第二解码器预热在 PLAY 之前就把解码器实例创建好避免第一帧到达时才初始化那一下的耗时往往有几百毫秒。第三首帧优先收到第一个关键帧IDR之前不解码非关键帧直接丢弃等 IDR 到了再开始这样能避免花屏也能让首帧更快出来。稳定性方面我习惯加一个「心跳检测」如果超过一定时间比如 3 秒没有收到任何 RTP 包就主动发 OPTIONS 或 GET_PARAMETER 保活确认连接还在。很多 RTSP 服务端有会话超时长时间没有 RTSP 信令交互会主动断开光靠 RTP 数据流不算保活。这个坑我在多个项目里都踩过表现是播放几分钟后突然断流日志里没有任何错误其实就是服务端把会话回收了。还有一个技巧是 RTP 序列号连续性检查。收到包时记录序列号如果发现跳变说明中间丢包了。对于 UDP 模式丢包后不要傻等直接请求关键帧发 RTCP 的 PLI 或 FIR让服务端尽快补一个 IDR 过来比等下一个自然关键帧快得多。这个机制在弱网下对体验提升很明显。最后说个参数上的细节RTP 收包缓冲区大小。Windows 默认的 UDP 接收缓冲区可能只有 8KB高码率流下容易溢出丢包。可以在 SETUP 之后用setsockopt把SO_RCVBUF调到 256KB 甚至更大具体值要看码率和网络抖动情况。这个调整对 UDP 模式的稳定性帮助很大但很多人不知道要调。我自己的习惯是每接一路新流先用 ffprobe 把编码、分辨率、音频参数全部确认一遍再决定传输模式和解码策略而不是上来就写代码。这个习惯帮我省掉了大量「代码没问题但流就是不对」的排查时间。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

编译原理课设全流程实战:从词法分析到目标代码生成

编译原理课设全流程实战:从词法分析到目标代码生成

简介:这份资源是华中科技大学2019级编译原理实验的源码成果,面向正在学习编译原理、需要动手实现编译器前端的高校学生与自学者,尤其适合对照课程实验进度进行参考与二次开发。项目围绕词法分析、语法分析、符号表管理、静态语义分析、中间代…

2026/10/12 17:18:34 阅读更多 →
open-code-review:把代码审查变成可沉淀的工程资产

open-code-review:把代码审查变成可沉淀的工程资产

代码审查大概是整个研发流程里最容易被消耗掉价值的一环。我见过不少团队把代码审查写进制度,每次合并前必须有一个人点通过,结果呢?白天各忙各的,晚上打开待审查的改动列表,里面躺着十几个几百行的大文件,…

2026/10/12 17:18:34 阅读更多 →
Java停车场系统实战:PostgreSQL并发分配与状态机建模

Java停车场系统实战:PostgreSQL并发分配与状态机建模

简介:本资源是一份面向计算机专业本科生与Java初学者的课程设计/毕业设计参考文档,聚焦城市停车难问题,提供基于Java技术栈的停车场管理系统完整设计方案。文档涵盖课题背景意义、国内外研究综述、SpringBootVue前后端框架选型依据、MySQL数据…

2026/10/13 20:10:59 阅读更多 →

最新新闻

三强版 Codex 一键部署包,本地桌面 AI 自动化全流程实操

三强版 Codex 一键部署包,本地桌面 AI 自动化全流程实操

🔍前言 本文介绍 Codex Harness 一键安装包的部署方案,无需解压,也无需繁琐的环境配置,新手也能快速上手。该方案同时支持 Windows 与 macOS 双平台,跟随向导几步即可完成安装、登录与模型密钥配置。文中完整演示了技…

2026/10/13 20:11:08 阅读更多 →
SpringBoot多环境日志配置:用logback-spring.xml+Profile实现环境隔离

SpringBoot多环境日志配置:用logback-spring.xml+Profile实现环境隔离

1. 为什么SpringBoot项目要把日志配置拆成多环境不管你信不信,我见过太多SpringBoot项目,日志配置就一个logback.xml躺在resources目录里,从入职第一周用到项目上线,开发环境一堆DEBUG刷屏没人管,生产环境出了问题想查…

2026/10/13 20:11:07 阅读更多 →
MATLAB中AES-256加解密实战:Java桥接与文件安全

MATLAB中AES-256加解密实战:Java桥接与文件安全

简介:这份资源面向学习密码学与MATLAB编程的读者,提供AES-256位加密算法在MATLAB 2012B环境下的完整实现代码,帮助理解对称加密的内部运作机制。压缩包共16个文件,约448KB,以13个m脚本为核心,涵盖密钥扩展、…

2026/10/13 20:11:07 阅读更多 →
C++结构体与结构体数组:从定义、内存布局到排序实践

C++结构体与结构体数组:从定义、内存布局到排序实践

如果你翻过C教材的目录,多半会在“结构体”这一章停几秒钟,然后想:这不就是把几个变量捆在一起吗?有什么好讲的。我工作之后才意识到,结构体恰恰是C里最容易被低估的基础设施,链表、树、图形控件的属性表、…

2026/10/13 20:11:07 阅读更多 →
Spring Boot JPA 实战指南:从实体映射到查询优化的避坑经验

Spring Boot JPA 实战指南:从实体映射到查询优化的避坑经验

1. 为什么我要认真聊聊 Spring Boot 里的 JPA做后端开发这些年,我接触过的持久层方案不算少。从最早手写 JDBC 拼 SQL,到后来用 MyBatis 写 XML 映射文件,再到 Spring Boot 项目里大量使用 JPA,每一种方案都有它适合的场景。但要说…

2026/10/13 20:11:07 阅读更多 →
Sufer 8.0英文版等值线图实战:从散点插值到批量出图的完整避坑指南

Sufer 8.0英文版等值线图实战:从散点插值到批量出图的完整避坑指南

简介:Surfer 8.0英文版是Golden Software公司推出的专业地形图绘制与三维建模软件,面向地质、环境科学、工程及地球科学领域的从业者与学习者,用于将离散数据转化为连续格网表面并生成专业图件。压缩包共234个文件,约12.41MB&…

2026/10/13 20:10:07 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 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/12 21:05:09 阅读更多 →
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/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练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/12 18:11:17 阅读更多 →