MP4 文件结构深入解析
- 如果是元数据在文件开头,那么[mdat]和[moov]替换位置
+---------------------------------------------------------------+
| MP4 文件结构 |
| |
| +--------+ +--------+ +--------+ |
| | [ftyp] | -------> | [mdat] | -------> | [moov] | |
| +--------+ +--------+ +--------+ |
| 文件类型标识 媒体数据 元数据容器 |
| | |
| +---------------------+ |
| | |
| +--------------------------------------------------+ |
| | [moov] | |
| | +-----------------------------+ | |
| | | [mvhd] | | |
| | +-----------------------------+ | |
| | 影片头部信息 | |
| | | |
| | +-----------------------------+ | |
| | | [trak] | | |
| | +-----------------------------+ | |
| | 轨道信息 | |
| | / \ | |
| | / \ | |
| | +-------------+ +-------------+ | |
| | | [tkhd] | | [mdia] | | |
| | +-------------+ +-------------+ | |
| | 轨道头信息 媒体信息 | |
| | | | |
| | | | |
| | +------------------+ | |
| | | [minf] | | |
| | +------------------+ | |
| | 媒体信息容器 | |
| | / \ | |
| | / \ | |
| | +-------------+ +-------------+ | |
| | | [vmhd/smhd] | | [stbl] | | |
| | +-------------+ +-------------+ | |
| | 视频/音频信息 样本表 | |
| | | | |
| | +------------------+ | |
| | | | | |
| | | +-------------+ | | |
| | | | [stsd] | | | |
| | | +-------------+ | | |
| | | 样本描述 | | |
| | | | | |
| | | +-------------+ | | |
| | | | [stts] | | | |
| | | +-------------+ | | |
| | | 时间到样本映射 | | |
| | | | | |
| | | +-------------+ | | |
| | | | [stss] | | | |
| | | +-------------+ | | |
| | | 同步样本表 | | |
| | | | | |
| | | +-------------+ | | |
| | | | [stsz] | | | |
| | | +-------------+ | | |
| | | 样本大小 | | |
| | +------------------+ | |
| +--------------------------------------------------+ |
| |
+---------------------------------------------------------------+
HTTP Range 请求机制详解
HTTP Range
请求是HTTP/1.1
协议中的一个重要特性,允许客户端请求资源的部分内容。这对于大型媒体文件(如视频)的传输尤为重要。
Range 请求头格式与语法
Range: bytes=<start>-<end>
播放支持 http-range 的视频
1. 发送一个 GET 请求 (with range)
Chrome Server
+----------------------------+ +--------------------------------------------------+
| GET /video.mp4 HTTP/1.1 | | HTTP/1.1 206 Partial Content |
| Host: cdn.com | <--------> | Accept-Ranges: bytes |
| Range: bytes=0- | | Content-Range: bytes 0-11799707/828908176 |
+--------------------------+ | Content-Length: 827040401 |
| (body: .........) |
+--------------------------------------------------+
2. 当你快进播放的时候
Chrome Server
+--------------------------------+ +-----------------------------------------------------+
| GET /a.mp4 HTTP/1.1 | <--------> | HTTP/1.1 206 Partial Content |
| Host: cdn.com | | Accept-Ranges: bytes |
| Range: bytes=1867776-828908176 | | Content-Range: bytes 1867776-828908176/828908177 |
+--------------------------------+ | Content-Length: 827040400 |
| (body: .........) |
+-----------------------------------------------------+
播放不支持 http-range 的视频
- GET视频流式播放, 不能快进快退, 每次从头开始播放
- 服务器需要配置 Accept-Ranges: none 或不返回 Accept-Ranges 头
- 浏览器检测到不支持 Range 请求时,会采用线性播放模式
元数据位置对播放的影响
- MP4文件中moov box的位置对视频播放有重大影响:
-
元数据在文件开头 (faststart) :
- 浏览器可以立即获取视频的关键信息
- 无需下载整个文件即可开始播放
- 支持快速定位到任意时间点
-
元数据在文件结尾 :
- 浏览器需要下载完整文件或使用Range请求获取文件末尾
- 在获取元数据前无法确定视频时长和其他关键信息
- 影响用户体验,尤其是大文件
生成视频测试
## ## 元数据在开始
ffmpeg -f lavfi -i testsrc=duration=60:size=1920x1080:rate=30 -c:v libx264 -c:a aac -movflags +faststart meta_at_start.mp4
## 元数据在末尾
ffmpeg -f lavfi -i testsrc=duration=60:size=1920x1080:rate=30 -c:v libx264 -c:a aac -movflags empty_moov+default_base_moof meta_at_end.mp4
测试结果
- 下面表格的顺序是上面的图
视频 | 描述 | 播放过程 |
---|---|---|
meta_at_start.mp4 | 元信息在开头 | 一开始就能播放, 不能快进快退(只能流式播放) |
meta_at_end.mp4 | 元信息在结尾 | 一开始就能播放, 不能快进快退(显示视频时长 0:08 有问题,因为没加载元信息) |
meta_at_start_with_range.mp4 | 元信息在开头并且支持 range | 一开始就能播放, 可以快进快退 |
meta_at_end_with_range.mp4 | 元信息在结尾并且支持 range | 一开始不能播放,必须下载完成才能播放 (下载完了可以快进快退) |
HLS 与 M3U8 流媒体技术
除了基于 HTTP Range 的视频流媒体播放,另一种广泛使用的技术是 HTTP Live Streaming (HLS),它使用 M3U8 播放列表文件来管理视频分片。
+--------------------------------------------------------------------------------------+
| M3U8 文件结构 |
| |
| 主播放列表 (master.m3u8) |
| +--------------------------------------------------------------------------------+ |
| | #EXTM3U | |
| | #EXT-X-VERSION:3 | |
| | #EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080 | |
| | stream_0.m3u8 | |
| | #EXT-X-STREAM-INF:BANDWIDTH=3000000,RESOLUTION=1280x720 | |
| | stream_1.m3u8 | |
| | #EXT-X-STREAM-INF:BANDWIDTH=1000000,RESOLUTION=640x360 | |
| | stream_2.m3u8 | |
| +--------------------------------------------------------------------------------+ |
| | |
| +-------------+-------------------------------------------+ |
| | | | |
| v v v |
| +------------------------+ +------------------------+ +------------------------+ |
| | 高清播放列表 | | 标清播放列表 | | 低清播放列表 | |
| | stream_0.m3u8 | | stream_1.m3u8 | | stream_2.m3u8 | |
| | | | | | | |
| | #EXTM3U | | #EXTM3U | | #EXTM3U | |
| | #EXT-X-VERSION:3 | | #EXT-X-VERSION:3 | | #EXT-X-VERSION:3 | |
| | #EXT-X-TARGETDURATION:6| | #EXT-X-TARGETDURATION:6| | #EXT-X-TARGETDURATION:6| |
| | #EXT-X-MEDIA-SEQUENCE:0| | #EXT-X-MEDIA-SEQUENCE:0| | #EXT-X-MEDIA-SEQUENCE:0| |
| | #EXTINF:6.0, | | #EXTINF:6.0, | | #EXTINF:6.0, | |
| | data000.ts | | data000.ts | | data000.ts | |
| | #EXTINF:6.0, | | #EXTINF:6.0, | | #EXTINF:6.0, | |
| | data001.ts | | data001.ts | | data001.ts | |
| | ... | | ... | | ... | |
| | #EXT-X-ENDLIST | | #EXT-X-ENDLIST | | #EXT-X-ENDLIST | |
| +------------------------+ +------------------------+ +--------------------+ |
| | | | |
| v v v |
| +------------+ +------------+ +------------+ |
| | 高清片段 | | 标清片段 | | 低清片段 | |
| | data000.ts | | data000.ts | | data000.ts | |
| | data001.ts | | data001.ts | | data001.ts | |
| | ... | | ... | | ... | |
| +------------+ +------------+ +------------+ |
| |
+----------------------------------------------------------------------------------------+
-
视频分段:将视频切分为多个小片段(通常为 .ts 文件)
- 每个片段通常为 6-10 秒
- 片段时长影响延迟和带宽适应性
- 较短片段利于快速切换,但会增加请求次数
- 较长片段减少请求次数,但增加首次加载延迟
-
创建索引文件:生成 .m3u8 文件,包含所有片段的信息和播放顺序
- 主播放列表(master.m3u8):包含所有清晰度版本信息
- 媒体播放列表(stream_N.m3u8):包含具体片段信息
- 播放列表定期更新,支持直播场景
-
自适应比特率:为同一视频提供多种分辨率和比特率的版本
- 高清流(1080p):BANDWIDTH=5000000, RESOLUTION=1920x1080
- 标清流(720p):BANDWIDTH=3000000, RESOLUTION=1280x720
- 低清流(360p):BANDWIDTH=1000000, RESOLUTION=640x360
- 播放器根据网络状况自动切换合适的清晰度
-
客户端请求:播放器先请求 .m3u8 文件,然后根据需要下载各个片段
- 首次请求 master.m3u8 获取可用清晰度列表
- 根据当前网络状况选择合适的清晰度流
- 请求对应的 stream_N.m3u8 获取片段列表
- 按需下载 .ts 片段文件
- 网络状况变化时自动切换清晰度
与 HTTP Range 的比较
对比维度 | HLS (M3U8) | HTTP Range |
---|---|---|
文件格式 | 将视频分割为多个 .ts 片段 | 使用单一 MP4 文件 |
优点 | • 支持自适应码率 • 广泛兼容各种设备 • 原生支持直播场景 • 支持清晰度动态切换 |
• 延迟低,精确请求 • 存储高效,单一文件 • 实现简单 • 支持精确定位 • 带宽利用率高 |
缺点 | • 延迟较高(至少一个片段) • 存储开销大 • 实现复杂 • 存在带宽冗余 |
• 不支持自适应码率 • 兼容性受限 • 缓存效率较低 • 不适合直播场景 |
适用场景 | • 直播业务 • 多设备支持 • 不稳定网络环境 • 需要多码率支持 |
• 点播业务 • 低延迟要求 • 存储受限场景 • 简单播放需求 |
技术复杂度 | 高:需要完整的转码和分发系统 | 低:仅需支持 Range 请求 |
运维成本 | 高:需要维护转码集群和存储系统 | 低:普通 Web 服务器即可 |