7.1.1 裸码流缺什么(What Elementary Streams Lack)

给 "容器" 下定义之前,不妨先做一场思想实验:把第六章学到的编码器全都跑起来——视频走 x264,音频走 AAC——然后把两路输出原样存盘、发给观众。这条链路上会接连冒出四个绕不开的困境。它们凑在一起,恰好勾勒出容器的全部职责。

困境一:想跳到第 3 分钟,从哪儿开始读?

观众拖动进度条,是再自然不过的操作。可对裸码流而言,这个简单的需求近乎无解:H.264 裸流是一串前后勾连的 NALU 序列,想定位 "第 180 秒的画面",文件里没有任何地方写着 "第 180 秒对应第几个字节"。唯一的办法是从头扫描,边解码边数帧——更要命的是,即便扫到了那一刻的码流位置,落点大概率是一个 P 帧或 B 帧,没有它引用的前序 I 帧,解码根本无法开始

所以容器的第一份职责是 索引(Indexing):建立 "时刻 → 字节偏移" 的映射表,并且索引的落点必须锚定在可以独立解码的关键帧上。MP4 的 stbl 五表(7.1.2)把这份职责做到了极致;FLV 与 TS 则各用一半的办法(7.1.3、7.1.4),个中取舍,恰是三容器设计取向分野的起点。

困境二:音视两条流,各走各的钟

回忆 5.3.1 的结论:音频与视频各有各的采样节拍,播放时必须对齐到同一条时间轴上,否则唇音失同步。可裸码流恰恰 没有统一的时间轴:AAC 裸流的每一帧只有 "1024 个采样" 的隐含时长,H.264 裸流更是连显示顺序都要靠 B 帧重排(6.2)之后才能确定——两条流面对面站着,谁也说不清 "你的第 1 秒是我的第几秒"。

容器的第二份职责因而是 同步(Synchronization):给每个被封装的最小单元(帧或采样块)盖上时间戳,让播放器拿到一份明确的 "何时解、何时显" 时刻表。这份时刻表在不同容器里形态各异——MP4 用各轨 timescale 下的增量表,FLV 用毫秒级绝对时间戳,TS 用 90 kHz 的 PTS/DTS 配 27 MHz 的 PCR——但职责只有一个。

困境三:这个文件 "是什么"?

双击一个文件之前,播放器需要回答一串问题:视频是 H.264 还是 H.265?分辨率多少?帧率多少?音轨是 AAC 还是 MP3,几声几道?这些信息有一部分确实藏在裸码流深处(比如 SPS),但要拿到它们,得先按对应编码规格的语法逐层解析——还没决定用什么解码器,就得先会解这种编码,这是个先有鸡还是先有蛋的尴尬循环。更别提标题、时长、封面这些与编码无关的描述信息,裸码流里根本没有容身之处。

容器的第三份职责是 自描述(Self-description):在码流之外辟出一块独立的区域,用统一的格式登记编码类型、参数配置与业务元数据。播放器读几行 "档案" 就知道该请哪个解码器、如何初始化,不必先钻进码流里猜。

困境四:音频和视频,怎么 "打包旅行"?

最后一个困境藏得很朴素:如果把音视两路裸码流存成两个文件分别传输,配对、同步下载、断点续传全都成了观众的负担。直播场景更极端——数据是一条连续不断的流,根本没有 "两个文件" 的概念,声音与画面必须在同一条字节流里交替出现,接收端边收边解。

容器的第四份职责是 复用(Multiplexing):把多路码流切成小单元,按一定规则交织进同一条字节流,并给每个单元贴上 "我属于哪路流" 的标签。MP4 用 trak 静态分轨,FLV 用 Tag 类型分流,TS 用 PID 动态复用——交织的粒度与标签的形式,同样是场景说了算。

小结:容器的三大件

四个困境归并起来,容器的工作清单其实只有三条主线:

索引(定位) + 同步(时间) + 复用(打包),外加一块自描述的元数据

接下来的三节,我们看三份风格迥异的答卷:MP4 把索引做到极致,服务于存储与点播的随机访问;FLV 把结构削到最简,服务于流式场景的顺序消费;MPEG-TS 则把容错刻进每个字节,服务于一次发出、无法重来的广播信道。没有哪种容器是 "最好的",只有与场景最合拍的

Copyright © Since 2021 李述博 (Arikan.Li) , All Rights Reserved all right reserved,powered by GitbookLast Updated: 2026-08-26 08:25:15

results matching ""

    No results matching ""