H264/H265码流类型详解:Annex-B与AVCC格式原理、转换及避坑指南
1. 码流类型到底在聊什么刚接触音视频编解码这块的朋友看到“H264/H265码流类型”这个词第一反应往往是懵的。码流不就是视频数据吗怎么还有类型之分我当初也是这么想的直到有一次在项目里做流媒体转发发现同样的H264数据有的播放器能直接解有的死活黑屏排查了大半天才意识到——问题出在码流类型上。简单来说码流类型指的是编码后的视频数据在传输和存储时采用的封装组织方式。H264和H265作为两大主流视频编码标准它们产出的裸码流并不是一坨无结构的数据而是按照特定规则组织起来的。这个组织规则的不同就产生了不同的码流类型。最常见的两种分类维度是Annex-B格式和AVCC格式以及裸流和封装流。这篇文章适合谁看如果你在做音视频开发、流媒体服务搭建、播放器适配或者只是单纯想搞明白为什么同一个视频文件换个场景就播不了那这篇内容应该能帮你省下不少排查时间。我会从实际项目出发把H264和H265的码流类型掰开揉碎讲清楚包括它们长什么样、怎么判断、怎么转换、踩过哪些坑。2. 为什么码流类型这么重要2.1 一个真实踩坑场景之前做过一个跨平台播放项目服务端用某开源工具把视频转成H264裸流推给客户端。测试的时候在PC浏览器上一切正常结果到了移动端播放器上直接花屏。抓包一看数据本身没问题解码器也没报错但就是渲染不出来。后来定位到原因服务端输出的是Annex-B格式的码流而移动端播放器的解码接口期望的是AVCC格式。两种格式的起始码和NALU长度描述方式完全不同解码器拿到不认识的格式自然就罢工了。这个坑让我意识到码流类型不是学术概念而是直接影响能不能跑起来的关键细节。很多文档只告诉你H264怎么编码却不告诉你编码后的数据该怎么组织、怎么传给下游结果就是各种“玄学”问题。2.2 不同场景对码流类型的偏好为什么会有不同的码流类型因为不同场景的需求不一样。实时流传输场景比如视频通话、直播推流通常用Annex-B格式。这种格式用起始码来分隔每个NALU解析起来简单直接适合边收边解。你不需要知道整个流有多长收到一个起始码就知道一个新的NALU开始了。文件存储和MP4封装场景通常用AVCC格式。这种格式在NALU前面加长度字段方便随机访问和跳转。MP4文件里的视频轨道几乎都是AVCC格式的。还有一种是裸流和封装流的区别。裸流就是纯编码数据没有容器包裹封装流则是放在MP4、MKV、TS等容器里的数据。容器会决定码流以什么形式存储比如MP4用AVCCTS用Annex-B。注意判断码流类型不能只看编码标准H264和H265都有Annex-B和AVCC两种形式关键看数据是怎么组织的。3. H264码流类型深度拆解3.1 Annex-B格式起始码分隔一切Annex-B格式的核心特征是起始码。每个NALU前面会有一个起始码通常是0x00000001或0x000001。解码器通过扫描起始码来切分NALU不需要额外的长度信息。一个典型的Annex-B码流长这样00 00 00 01 67 64 00 1F ... (SPS) 00 00 00 01 68 EB EC B2 ... (PPS) 00 00 00 01 65 88 84 ... (IDR帧数据) 00 00 00 01 41 9A 02 ... (非IDR帧数据)每个00 00 00 01后面跟着的就是一个NALU。第一个字节是NALU头包含NALU类型等信息。对于H264NALU类型在第一个字节的低5位对于H265NALU类型在第一个字节的1到6位这个差异后面会细说。Annex-B格式的优点很明显流式解析友好。你不需要预先知道每个NALU的长度收到数据就扫描起始码扫到了就切分。这对于实时传输场景非常合适因为数据是源源不断来的你没法预知下一帧有多大。但缺点也很明显随机访问困难。如果你想跳到视频的第10分钟用Annex-B格式就得从头扫描起始码效率很低。而且起始码本身会占用额外字节虽然不多但在海量数据下也是开销。3.2 AVCC格式长度字段说了算AVCC格式的核心特征是长度前缀。每个NALU前面用固定字节数通常是4字节表示这个NALU的长度没有起始码。一个典型的AVCC码流长这样00 00 00 1A 67 64 00 1F ... (长度0x1A后面是SPS) 00 00 00 08 68 EB EC B2 ... (长度0x08后面是PPS) 00 00 12 34 65 88 84 ... (长度0x1234后面是IDR帧数据)每个NALU前面的4个字节是大端序的长度值解码器先读长度再读对应字节数的数据。这种方式随机访问非常方便因为你可以直接跳到某个位置读长度然后跳过对应字节数找到下一个NALU。AVCC格式通常还会配合extradata使用。extradata里存放SPS和PPS不放在码流里。这样每个NALU就纯粹是帧数据结构更紧凑。MP4文件里的avcC盒子就是干这个的它记录了SPS、PPS以及NALU长度字段的字节数。提示AVCC格式的NALU长度字段字节数不固定可能是1、2、4字节具体看extradata里的lengthSizeMinusOne字段。大多数情况是4字节但别假设一定是4。3.3 两种格式的转换实操实际项目中经常需要在Annex-B和AVCC之间转换。比如从RTSP流Annex-B拿到数据要存成MP4文件AVCC就得做转换。Annex-B转AVCC的思路扫描起始码切分出所有NALU提取SPS和PPS放入extradata对每个NALU去掉起始码在前面加上4字节长度把处理后的NALU拼接起来用Python写个简化版转换逻辑def annexb_to_avcc(data): nals [] i 0 while i len(data): # 找起始码 if data[i:i4] b\x00\x00\x00\x01: start i 4 i 4 elif data[i:i3] b\x00\x00\x01: start i 3 i 3 else: i 1 continue # 找下一个起始码 j i while j len(data): if data[j:j4] b\x00\x00\x00\x01 or data[j:j3] b\x00\x00\x01: break j 1 nals.append(data[start:j]) i j # 转成AVCC result b for nal in nals: result len(nal).to_bytes(4, big) nal return resultAVCC转Annex-B就反过来读4字节长度取出NALU在前面加起始码。def avcc_to_annexb(data): result b i 0 while i len(data): length int.from_bytes(data[i:i4], big) i 4 nal data[i:ilength] result b\x00\x00\x00\x01 nal i length return result这两个函数是最简版本实际用的时候要处理边界情况比如数据不完整、长度字段字节数不是4等。3.4 H264 NALU类型速查理解码流类型绕不开NALU类型。H264的NALU头是1字节低5位是类型NALU类型值名称说明1非IDR片普通帧数据5IDR片关键帧可独立解码6SEI补充增强信息7SPS序列参数集8PPS图像参数集9AUD访问单元分隔符SPS和PPS是解码的“说明书”没有它们解码器不知道分辨率、帧率等信息。IDR帧是随机访问的入口播放器跳转时通常找最近的IDR帧开始解码。实操心得有些场景下SPS/PPS会重复发送比如每个IDR帧前都带一份。这不是冗余而是为了容错——万一中间丢了后面还能恢复。但存文件时通常只保留一份放在extradata里。4. H265码流类型与差异对比4.1 H265的NALU头变化H265的NALU头和H264不一样变成了2字节。类型字段在第一个字节的1到6位共6位能表示更多类型。第二个字节包含层ID和时域ID等信息。H265常见NALU类型NALU类型值名称说明0-9非IRAP普通帧数据16-21IRAP关键帧相关32VPS视频参数集33SPS序列参数集34PPS图像参数集39SEI补充增强信息注意H265多了个VPS视频参数集。它比SPS层级更高描述整个视频序列的全局信息。解码H265时VPS、SPS、PPS三者缺一不可。4.2 H265的Annex-B和AVCCH265同样有Annex-B和AVCC两种格式起始码和长度前缀的逻辑跟H264一样。区别在于NALU头是2字节解析类型时要取第一个字节的1-6位extradata结构更复杂包含VPS、SPS、PPS三组参数AVCC格式的extradata里参数集数量可能不止三个判断H265码流类型的方法跟H264类似看有没有起始码。有起始码就是Annex-B有长度前缀就是AVCC。4.3 H264和H265码流类型对比表对比项H264H265NALU头长度1字节2字节类型字段位置低5位第一个字节的1-6位参数集SPS、PPSVPS、SPS、PPS关键帧类型IDR5IRAP16-21Annex-B起始码0x000001或0x00000001同左AVCC长度前缀通常4字节通常4字节extradata内容avcC盒子hvcC盒子这个表建议存下来排查问题时对照看能快速定位是哪种码流、哪个环节出了问题。注意H265的IRAP类型范围是16到21其中16是BLA17-18是IDR19-20是CRA21是保留。不同关键帧类型的随机访问行为有差异做seek的时候要注意。5. 实战如何判断和转换码流类型5.1 快速判断码流类型的脚本拿到一段码流怎么知道它是Annex-B还是AVCC写个脚本扫一下def detect_stream_type(data): # 检查开头是否有起始码 if data[:4] b\x00\x00\x00\x01 or data[:3] b\x00\x00\x01: return Annex-B # 检查是否是AVCC读4字节长度看是否合理 if len(data) 4: length int.from_bytes(data[:4], big) if 0 length len(data) - 4: # 进一步验证跳过length后下一个长度是否也合理 next_pos 4 length if next_pos 4 len(data): next_length int.from_bytes(data[next_pos:next_pos4], big) if 0 next_length len(data) - next_pos - 4: return AVCC return Unknown这个脚本的逻辑是先看有没有起始码有就是Annex-B没有就假设是AVCC读长度字段验证一下。如果连续两个长度都合理基本可以确定是AVCC。实际用的时候建议多验证几个NALU避免误判。有些数据开头可能是SEI或其他类型长度字段看起来可能不“合理”多试几个位置更稳。5.2 从容器中提取码流类型信息如果你拿到的是MP4文件可以用工具看它的码流类型。比如用ffprobeffprobe -show_streams -select_streams v input.mp4输出里会显示codec_tag_string如果是avc1就是H264 AVCChvc1或hev1就是H265 AVCC。MP4里的视频轨道基本都是AVCC格式因为容器需要随机访问。TS流则通常是Annex-B格式因为TS是流式传输用起始码分隔更自然。你可以用ffprobe -show_streams -select_streams v input.ts看codec_tag_string如果是avc1但实际数据是Annex-B说明容器和码流格式不匹配播放器可能会出问题。5.3 转换时的注意事项做码流类型转换时有几个坑我踩过第一别忘了参数集。Annex-B转AVCC时SPS和PPS要从码流里提取出来放到extradata。如果忘了这一步AVCC格式的码流就没有解码所需的参数集播放器直接黑屏。第二长度字段字节数要对齐。AVCC的extradata里有个lengthSizeMinusOne字段表示长度字段用几个字节。大多数是4字节但有些实现用2字节甚至1字节。转换时要跟下游对齐不然长度解析全错。第三起始码可能是3字节或4字节。Annex-B的起始码有0x000001和0x00000001两种。扫描的时候两种都要认不然会漏掉NALU。有些编码器会在每个NALU前用4字节有些用3字节甚至混用。第四H265的VPS不能丢。H265的extradata里必须包含VPS、SPS、PPS三组。少一个都可能导致解码失败。有些工具只提取SPS和PPS忘了VPS结果H265播不了。实操心得转换完成后用ffplay或VLC实际播一下别只看数据长度对不对。播放器能正常渲染才算真正转换成功。6. 常见问题与排查技巧6.1 播放器黑屏/花屏排查思路遇到播放器黑屏或花屏按这个顺序排查确认码流类型用上面的脚本判断是Annex-B还是AVCC跟播放器期望的是否一致检查参数集SPS/PPSH265还要VPS是否存在且完整验证NALU完整性有没有截断的NALU长度字段是否越界看解码器日志大多数解码器会输出具体错误比如“missing SPS”或“invalid NALU length”我遇到过一种情况码流类型是对的参数集也有但播放器就是花屏。最后发现是NALU顺序错了——SPS和PPS必须在IDR帧之前如果顺序颠倒解码器可能来不及配置就遇到IDR帧导致解码异常。6.2 常见问题速查表问题现象可能原因排查方法黑屏无画面码流类型不匹配检查Annex-B/AVCC是否与播放器期望一致花屏/绿屏参数集缺失或错误确认SPS/PPS/VPS存在且内容正确播放几秒后卡住NALU长度字段错误验证AVCC长度字段字节数和值只有第一帧能播关键帧间隔问题检查IDR/IRAP帧是否定期出现音频正常视频异常视频码流类型问题单独提取视频轨道分析H265播不了VPS缺失确认extradata包含VPS6.3 独家避坑技巧技巧一用十六进制编辑器直接看。拿到一段码流用hex编辑器打开看开头几个字节。如果是00 00 00 01或00 00 01基本就是Annex-B。如果是其他值大概率是AVCC的长度字段。这个方法最快不需要写代码。技巧二抓包时同时保存原始数据。排查流媒体问题时别只看日志把原始码流dump下来。日志可能骗人但原始数据不会。我习惯在关键节点加个dump逻辑把收到的数据存成文件事后用脚本分析。技巧三H265的NALU类型解析要小心。H265的NALU头是2字节类型在第一个字节的1-6位。解析时用(byte 1) 0x3F别用H264的byte 0x1F不然类型全错。技巧四转换后做round-trip验证。Annex-B转AVCC后再转回Annex-B对比原始数据。如果完全一致说明转换无损。如果有差异检查是不是参数集处理有问题。技巧五注意字节序。AVCC的长度字段是大端序但有些平台默认小端序。写转换代码时明确指定big别依赖默认行为。7. 工具选型与实操建议7.1 常用工具对比工具用途优点缺点ffmpeg格式转换、分析功能全支持H264/H265命令行参数多学习曲线陡ffprobe流信息分析输出详细易解析只能分析不能转换hex编辑器直接看数据直观无需代码大文件不方便自写脚本定制化分析灵活可集成需要开发时间VLC播放验证支持格式多错误信息不够详细ffmpeg是最常用的比如把Annex-B转AVCCffmpeg -i input.h264 -c:v copy -bsf:v h264_mp4toannexb output.mp4反过来ffmpeg -i input.mp4 -c:v copy -bsf:v h264_annexb_to_mp4 output.h264注意-c:v copy表示不重新编码只做格式转换。这样速度快而且无损。7.2 实操建议建议一先确定下游期望什么格式。做转换之前先搞清楚播放器或封装库期望Annex-B还是AVCC。别转了半天发现方向反了。建议二保留原始数据。转换前备份原始码流出问题了可以回退对比。建议三小数据量先验证。别一上来就处理几个G的文件先拿几帧数据测试转换逻辑确认没问题再批量处理。建议四关注extradata。AVCC格式的extradata是解码的关键转换时确保正确生成。可以用ffprobe查看extradata内容ffprobe -show_streams -select_streams v input.mp4 | grep extradata建议五H265转换用专门工具。H265的码流结构比H264复杂建议用ffmpeg的hevc_mp4toannexb和hevc_annexb_to_mp4别自己手写转换逻辑容易出错。提示ffmpeg的bitstream filter名字要记准H264是h264_mp4toannexbH265是hevc_mp4toannexb别搞混了。8. 个人经验总结码流类型这个问题说大不大说小不小。平时写业务代码可能接触不到但一旦涉及底层传输、格式转换、播放器适配就绕不开。我自己的经验是遇到播放问题先怀疑码流类型再怀疑参数集最后才怀疑解码器。这个排查顺序帮我省了很多时间。另外H264和H265虽然都是视频编码标准但码流层面的差异不少。H265多了VPSNALU头变成2字节类型字段位置也变了。做H265相关开发时别直接套用H264的经验该查文档就查文档。最后分享一个小技巧如果你不确定一段码流是什么类型用ffmpeg跑一下ffprobe -show_frames看它能不能正常解析出帧信息。能解析出来说明码流类型和参数集基本没问题解析报错就往码流类型方向排查。这个方法简单粗暴但很有效。

相关新闻

Formisch Methods API参考手册:23个表单操控方法一文全解

Formisch Methods API参考手册:23个表单操控方法一文全解

【免费下载链接】formisch The lightweight, schema-first, and fully type-safe form library for React, Solid, Vue, Svelte and more. 项目地址: https://gitcode.com/gh_mirrors/fo/formisch 点击查看 免费下载 Formisch Methods 是轻量级表单库 Formisch 提供…

2026/10/15 23:37:54 阅读更多 →
ISE 14.7与Spartan-3E:从Verilog约束到比特流的FPGA实战指南

ISE 14.7与Spartan-3E:从Verilog约束到比特流的FPGA实战指南

简介:ISE软件应用实例是一套面向FPGA初学者的实战资料包。以赛灵思Spartan-3E平台为背景,包含六个递进式项目,覆盖基本逻辑门、计数器与移位寄存器、有限状态机、数字信号处理、同步串口及存储器接口,完整演示从建立工程、编写硬件…

2026/10/15 23:37:54 阅读更多 →
Spring Boot农产品销售平台实战:从数据库设计到部署上线全解析

Spring Boot农产品销售平台实战:从数据库设计到部署上线全解析

做了几年Java后端,Spring Boot这套东西早就刻进肌肉记忆了。但每次看到“农产品销售平台”这类选题,还是忍不住想多说两句——不是因为技术多难,而是它太典型了,几乎是Java Web入门阶段的“集大成训练场”。从需求分析到数据库设计…

2026/10/15 23:36:53 阅读更多 →

最新新闻

ComfyUI 在 Python 3.13 下报 No module named ‘insightface‘ 装不上?手把手完整修复教程

ComfyUI 在 Python 3.13 下报 No module named ‘insightface‘ 装不上?手把手完整修复教程

ComfyUI 在 Python 3.13 下报 No module named insightface 装不上?手把手完整修复教程 【免费下载链接】ComfyUI The most powerful and modular diffusion model GUI, api and backend with a graph/nodes interface. The fastest local inference engine in the …

2026/10/16 1:32:39 阅读更多 →
VR-reversal 实战指南:用 MPV 插件把 3D 视频转 2D 观看、环视缩放并导出 2D 视频

VR-reversal 实战指南:用 MPV 插件把 3D 视频转 2D 观看、环视缩放并导出 2D 视频

音视频VR视频 【免费下载链接】VR-reversal VR-Reversal - Player for conversion of 3D video to 2D with optional saving of head tracking data and rendering out of 2D copies. 项目地址: https://gitcode.com/gh_mirrors/vr/VR-reversal 点击查看 免费下载 …

2026/10/16 1:32:39 阅读更多 →
改进QSGS四参数随机生长法构建三维多孔介质代码

改进QSGS四参数随机生长法构建三维多孔介质代码

①改进的QSGS四参数随机生长法构建三维多孔介质代码。②QSGS四参数随机生长法构建三维多孔介质代码。代码中包含26/8连通去除孤立孔隙(可定向提取xyz方向的连通孔隙),图像平滑,三维展示,切片展示。非均匀性&#xff08…

2026/10/16 1:32:39 阅读更多 →
GetQzonehistory:基于 Python 的 QQ 空间数据备份工具,把历史说说全量导出为 Excel

GetQzonehistory:基于 Python 的 QQ 空间数据备份工具,把历史说说全量导出为 Excel

GetQzonehistory:基于 Python 的 QQ 空间数据备份工具,把历史说说全量导出为 Excel 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 翻看旧说说时,图片…

2026/10/16 1:32:39 阅读更多 →
pdfplumber 页面渲染与可视化调试完整指南:4 个场景配好 PageImage 不踩坑

pdfplumber 页面渲染与可视化调试完整指南:4 个场景配好 PageImage 不踩坑

pdfplumber 页面渲染与可视化调试完整指南:4 个场景配好 PageImage 不踩坑 【免费下载链接】pdfplumber Plumb a PDF for detailed information about each char, rectangle, line, et cetera — and easily extract text and tables. 项目地址: https://gitcode.…

2026/10/16 1:32:39 阅读更多 →
BeanDefinition 合并

BeanDefinition 合并

0 前言1.BeanDefinition有父子关系,有个属性parentName,可设置BeanDefinition的父子关系 2.为啥要设置BeanDefinition的父子关系?因为子类可继承父类属性,抽出共性 3.BeanDefinition的合并过程就是将父子关系的 BeanDefinition 属…

2026/10/16 1:31:39 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

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

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 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/15 9:49:14 阅读更多 →