返回文章列表

第二章|容器格式 vs 编码格式:MP4、WebM、H.264、AAC 到底是什么关系

系统区分容器格式、编码格式、封装、解封装、裸流、MIME type 和 codec string,解释为什么文件后缀不能决定浏览器是否能播放。

第二章|容器格式 vs 编码格式

本章学习目标

学完这一章,你要能把下面这些容易混在一起的词彻底拆开:

容器 container
编码格式 codec
编码 encoding
解码 decoding
封装 muxing
解封装 demuxing
裸流 elementary stream / raw stream
MIME type
codec string

这一章最重要的结论是:

.mp4 不等于 H.264,.webm 不等于 VP9,.mp3 也不是像 MP4 那样的万能容器。

文件后缀只是外壳提示。真正决定浏览器能不能播的,通常是:

容器格式是否支持
+
里面的音视频 codec 是否支持
+
codec profile / level / bit depth / pixel format 是否支持
+
当前浏览器和系统是否有对应解码能力

MP4 的官方标准是 ISO/IEC 14496-14,它定义的是一种从 ISO Base Media File Format 派生出来的 MP4 文件格式,也就是容器层的规则,不等于某个具体视频编码算法。(ISO) WebM 是面向开放 Web 的多媒体容器,是 Matroska 的一个子集;Matroska 本身也是一种可扩展的音视频容器结构。(WebM Project) MP3 则更接近由 MPEG 音频帧组成的音频码流,IETF 对 audio/mpeg 的描述里明确说它是包含 MPEG frames 的 elementary byte stream,并且元数据通常是 ID3 这类额外块拼接在码流前后。([IETF Datatracker][3])


本章速览

这章最容易混乱的地方,是把“文件外壳”和“压缩算法”说成一回事。先用这张图分层:

容器格式 vs 编码格式:MP4、WebM、H.264、AAC 到底是什么关系 flow 1

本章可以压缩成三句话:

  • 容器负责“怎么装”,codec 负责“怎么压缩和还原”,两者是不同层。
  • 文件后缀只能提示容器,真正能不能播还要看浏览器是否支持容器、codec、profile、level、像素格式和系统解码能力。
  • muxing / demuxing 处理的是容器和轨道,encoding / decoding 处理的是压缩数据和原始媒体数据。

1. 先建立一个生活化类比

可以把一个媒体文件想成一个快递包裹:

快递纸箱        = 容器格式 container
箱子里的物品    = 编码后的音频 / 视频数据
打包过程        = 封装 muxing
拆箱过程        = 解封装 demuxing
把衣服压缩袋压扁 = 编码 encoding
把压缩袋恢复    = 解码 decoding

比如一个常见的 MP4 文件可以长这样:

movie.mp4
├── 容器:MP4
├── 视频轨:H.264 编码后的视频数据
├── 音频轨:AAC 编码后的音频数据
├── 字幕轨:可选
└── 元数据:时长、宽高、旋转角、时间戳、索引等

注意这里有两层:

文件怎么组织数据?      → 容器格式负责
音视频怎么压缩数据?    → codec 负责

所以说:

MP4 是盒子。
H.264 是视频压缩方法。
AAC 是音频压缩方法。

一句面试话术:

MP4 是容器格式,负责把视频轨、音频轨、字幕、元数据、时间戳等组织在一个文件里;H.264 是视频编码格式,负责把原始视频帧压缩成更小的编码数据。一个 MP4 文件里可以放 H.264 视频,也可以放 H.265、AV1 等其他视频编码,具体能不能播放取决于播放器是否同时支持容器和里面的 codec。


2. Container format 是什么

Container format,容器格式,解决的是:

多条媒体轨道和元数据,怎么放进一个文件里?

它通常负责这些事:

容器负责什么解释
轨道组织一个文件里可能有视频轨、音频轨、字幕轨、章节信息
时间信息每个 sample / frame 在什么时间播放
索引播放器 seek 到第 30 秒时,怎么快速找到对应数据
元数据时长、宽高、旋转角、语言、封面、title 等
数据块位置真正的媒体数据在文件哪个 offset
多路复用把音频、视频按时间关系交错存储

容器不一定关心视频画面怎么被压缩。它更像一个档案盒,负责说:

这里有一条视频轨。
这里有一条音频轨。
视频轨使用 avc1,也就是 H.264/AVC 的一种 sample entry。
音频轨使用 mp4a,也就是 MPEG-4 Audio / AAC 相关编码。
每个 sample 的时间戳是这样。
每个 sample 的数据在文件这里。

MP4 就是典型容器。WebM、MKV 也是典型容器。MP4 标准定义的是文件组织方式,WebM 项目文档也把 WebM 描述为多媒体容器格式。(ISO)


3. Codec 是什么

Codec 是:

coder + decoder

它可以理解成一套“压缩和还原规则”。

在音视频里,codec 通常做两件事:

原始数据 → 编码器 encoder → 压缩后的编码数据
压缩数据 → 解码器 decoder → 原始数据

视频例子:

一帧一帧的 RGB / YUV 原始画面
  ↓ H.264 encoder
H.264 encoded bitstream
  ↓ H.264 decoder
一帧一帧的 VideoFrame / YUV / RGB

音频例子:

PCM 采样数据
  ↓ AAC encoder
AAC encoded audio frames
  ↓ AAC decoder
PCM 采样数据

你可以把 codec 理解成压缩算法家族:

类型常见 codec
视频 codecH.264/AVC、H.265/HEVC、AV1、VP8、VP9
音频 codecAAC、MP3、Opus、Vorbis、FLAC
原始 / 未压缩表示PCM、Raw YUV、Raw RGB

H.264/AVC 是视频压缩标准,ISO/IEC 14496-10 与 ITU-T H.264 对应同一类高级视频编码标准。(The Library of Congress) H.265/HEVC 是另一代视频压缩标准,目标是更高压缩效率。(The Library of Congress) AV1 是 AOMedia 推出的开放视频 codec,面向高效视频压缩。(Alliance for Open Media) Opus 是 IETF RFC 6716 定义的音频 codec,面向语音、会议、游戏语音、音乐等交互式音频场景。([IETF Datatracker][7])


4. Muxing / Demuxing 是什么

4.1 Muxing:封装

Muxing,中文常叫:

封装
复用
多路复用

它做的是:

把已经编码好的音频数据、视频数据、字幕、元数据,按照某种容器格式写成一个文件。

比如:

H.264 视频编码数据
+
AAC 音频编码数据
+
时间戳 / 索引 / 轨道信息
  ↓ muxer
output.mp4

封装不会必然重新压缩画面。

也就是说,下面这个流程只是封装:

H.264 + AAC → MP4

如果视频本来就是 H.264,音频本来就是 AAC,只是把它们写进 MP4 容器,这叫 muxing,不叫重新编码。


4.2 Demuxing:解封装

Demuxing,中文常叫:

解封装
拆包
解复用

它做的是:

从容器里把音频轨、视频轨、字幕轨、元数据拆出来。

比如:

input.mp4
  ↓ demuxer
H.264 视频 samples
AAC 音频 samples
时间戳
track 信息
codec config

注意,demuxer 拿出来的通常还是 压缩后的编码数据,不是原始像素,也不是 PCM。

比如从 MP4 里 demux 出视频轨,你拿到的可能是:

EncodedVideoChunk / H.264 sample / access unit

而不是:

VideoFrame / RGB pixels / YUV pixels

真正把 H.264 变成 VideoFrame 的,是 decoder,不是 demuxer。WebCodecs 也是这个模型:它提供 VideoDecoder、VideoEncoder、AudioDecoder、AudioEncoder 等 codec 接口,用于在 raw frame/data 和 encoded chunk 之间转换;规范本身不强制浏览器必须支持某个具体 codec。(MDN Web Docs)


5. Encoding / Decoding 是什么

5.1 Encoding:编码

Encoding 是把原始音视频数据压缩成 codec 数据。

视频:

VideoFrame / Raw YUV / Raw RGB
  ↓ VideoEncoder / H.264 encoder
EncodedVideoChunk / H.264 bitstream

音频:

PCM / AudioData
  ↓ AudioEncoder / AAC encoder
EncodedAudioChunk / AAC frames

编码通常会涉及:

压缩率
画质 / 音质
码率
关键帧
GOP
延迟
硬件加速
profile / level

5.2 Decoding:解码

Decoding 是把 codec 数据还原成可处理的原始音视频数据。

视频:

H.264 / H.265 / AV1 encoded chunks
  ↓ decoder
VideoFrame / YUV / RGB

音频:

AAC / MP3 / Opus encoded chunks
  ↓ decoder
PCM / AudioData

浏览器播放视频时,大概就是:

下载 MP4
  ↓ demux
拆出 H.264 视频数据和 AAC 音频数据
  ↓ decode
视频变成帧,音频变成 PCM
  ↓ render / output
画到屏幕,送到声卡

6. 一张总流程图:从文件到播放

用户选择 movie.mp4


┌──────────────────┐
│ Container: MP4    │
│ - metadata        │
│ - video track     │
│ - audio track     │
│ - timestamps      │
└──────────────────┘

        │ demux 解封装

┌──────────────────┐       ┌──────────────────┐
│ H.264 samples     │       │ AAC samples       │
│ encoded video     │       │ encoded audio     │
└──────────────────┘       └──────────────────┘
        │                           │
        │ decode 解码                │ decode 解码
        ▼                           ▼
┌──────────────────┐       ┌──────────────────┐
│ VideoFrame        │       │ PCM / AudioData   │
│ raw video frame   │       │ raw audio samples │
└──────────────────┘       └──────────────────┘
        │                           │
        ▼                           ▼
   Canvas / GPU                Audio output
   屏幕显示                    扬声器播放

前端工程里最容易漏的一点是:

WebCodecs 接收的不是完整 MP4 文件。
WebCodecs 更关心 EncodedVideoChunk、EncodedAudioChunk、VideoFrame、AudioData。

所以如果输入是 MP4,你通常需要:

MP4 文件
  ↓ demuxer
EncodedVideoChunk / EncodedAudioChunk
  ↓ WebCodecs decoder
VideoFrame / AudioData

如果你最后要导出 MP4,你也需要:

VideoFrame / AudioData
  ↓ WebCodecs encoder
EncodedVideoChunk / EncodedAudioChunk
  ↓ muxer
MP4 文件

WebCodecs 是 codec 层 API,不是 MP4/WebM/MKV 容器解析器。W3C 对 WebCodecs 的定义是提供音频、视频、图像编码/解码接口,而不是规定某个容器格式的 muxer/demuxer。(W3C)


7. 常见格式关系表

名称类型常见后缀 / MIME它到底是什么常见搭配
MP4容器格式.mp4 / video/mp4 / audio/mp4存放音频、视频、字幕、元数据的文件结构H.264 + AAC、H.265 + AAC、AV1 + Opus/AAC 等
WebM容器格式.webm / video/webm / audio/webmMatroska 的子集,面向开放 WebVP8/VP9/AV1 + Vorbis/Opus
MKV容器格式.mkvMatroska 视频容器,更开放、可扩展H.264、H.265、AV1、AAC、Opus、字幕等
MP3音频编码格式 / 音频帧流.mp3 / audio/mpegMPEG Layer III 音频帧,常带 ID3 元数据通常就是单音频文件
AAC音频 codec.aac.m4a.mp4有损音频编码格式常放进 MP4 / M4A,也可用 ADTS
H.264 / AVC视频 codecavc1.h264、常在 .mp4视频压缩格式MP4、TS、MKV
H.265 / HEVC视频 codechvc1 / hev1、常在 .mp4 / .heic视频压缩格式,压缩效率通常高于 H.264,但兼容和授权更复杂MP4、HEIF、MKV
AV1视频 codecav01、常在 .webm / .mp4开放视频压缩格式WebM、MP4、MKV
Opus音频 codecopus、常在 .webm / .ogg低延迟、高适应性的音频 codecWebM、Ogg、Matroska
PCM原始音频表示 / 未压缩编码.wav 常见,但 WAV 是容器采样后的原始音频数据WAV、AudioData、Web Audio 内部处理

这里有几个关键点:

  1. MP4、WebM、MKV 是容器。
  2. H.264、H.265、AV1 是视频 codec。
  3. AAC、MP3、Opus 是音频 codec。
  4. PCM 是未压缩音频采样数据,经常是音频解码后的目标形态。
  5. MP3 文件虽然有文件形式,但它不像 MP4/MKV 那样是通用多轨容器。

WebM 官方文档说明它是 Matroska 的一个子集,Matroska 官方也说明 .webm 基于 Matroska;MP3 的 audio/mpeg 则由 RFC 3003 描述为 MPEG frames 的 elementary byte stream。(WebM Project) AAC 属于 MPEG-4 Audio 体系,MPEG 文档把 ISO/IEC 14496-3 描述为整合多类音频编码工具的音频标准。(MPEG)


8. 为什么 “MP4 里面可以放 H.264 视频 + AAC 音频”

因为 MP4 是容器,它允许文件里有多条 track。

一个典型 MP4 可以这样理解:

movie.mp4
├── ftyp:这个文件兼容哪些 MP4 brand
├── moov:元数据
│   ├── mvhd:整个 movie 的时间信息
│   ├── trak:视频轨
│   │   ├── codec: avc1 / H.264
│   │   ├── width / height
│   │   ├── duration
│   │   └── sample table
│   └── trak:音频轨
│       ├── codec: mp4a / AAC
│       ├── sample rate
│       ├── channel count
│       ├── duration
│       └── sample table
└── mdat:真正的媒体数据

这里:

MP4 负责:
- 这个文件里有几条轨
- 每条轨是什么类型
- 每条轨用什么 codec
- 每个 sample 在什么时候播放
- 每个 sample 在文件哪里

H.264 负责:
- 视频帧如何压缩
- I/P/B 帧如何组织
- 运动估计、帧间预测等编码细节

AAC 负责:
- PCM 音频如何压缩
- 频域数据如何表示
- 音频帧如何还原成 PCM

所以这句话是成立的:

MP4 里面可以放 H.264 视频 + AAC 音频。

但这句话不严谨:

MP4 是 H.264。

更准确的说法是:

这个 MP4 文件的视频轨使用 H.264 编码,音频轨使用 AAC 编码。

9. 为什么“浏览器能播放 MP4”不代表所有 MP4 都能播放

因为 video/mp4 只说明容器大概率是 MP4,不说明里面到底是什么 codec。

比如这几个文件都可能叫 MP4:

A.mp4 = H.264 Baseline + AAC-LC
B.mp4 = H.264 High 10-bit + AAC
C.mp4 = H.265 / HEVC + AAC
D.mp4 = AV1 + Opus
E.mp4 = MPEG-4 Part 2 Video + MP3

浏览器看到它们都是 .mp4,但支持情况可能完全不同。

真正需要问的是:

这个浏览器支持 MP4 容器吗?
这个浏览器支持里面的视频 codec 吗?
支持这个 codec 的 profile / level 吗?
支持 8-bit 还是 10-bit?
支持硬解还是只能软解?
音频 codec 支持吗?

MDN 的媒体类型文档也强调,单独的 MIME type 很模糊,因为同一种容器可能支持多种 codec、profile、level 等信息,因此可以在 MIME type 后面加 codecs 参数说明内容细节。(MDN Web Docs) HTMLMediaElement 的 canPlayType() 也允许传入 MIME type 和可选的 codecs 参数,并返回 ""maybeprobably 来表示当前设备播放可能性。(MDN Web Docs) RFC 6381 定义 codecsprofiles 参数,目的就是让接收端能从 Content-Type 判断里面使用了哪些 codec。([IETF Datatracker][13])


10. MIME type 和 codec string

10.1 MIME type 是什么

MIME type 是 Web 世界里描述资源类型的字符串。

常见媒体 MIME type:

video/mp4
audio/mp4
audio/mpeg
video/webm
audio/webm
video/ogg
audio/ogg

但是只写:

video/mp4

信息不够。它只说“这是 MP4 容器”,没说里面视频、音频分别是什么编码。

所以更好的写法是:

video/mp4; codecs="avc1.640028, mp4a.40.2"

这句话大概表示:

容器:MP4
视频:H.264 / AVC,codec string 是 avc1.640028
音频:AAC,codec string 是 mp4a.40.2

10.2 常见 codec string 示例

MIME + codecs大致含义
video/mp4; codecs="avc1.42E01E, mp4a.40.2"MP4 + H.264 Baseline-ish + AAC-LC
video/mp4; codecs="avc1.640028, mp4a.40.2"MP4 + H.264 High Profile Level 4.0 + AAC-LC
video/mp4; codecs="hvc1.1.6.L120.90, mp4a.40.2"MP4 + HEVC + AAC,实际支持依赖浏览器和系统
video/mp4; codecs="av01.0.08M.08, mp4a.40.2"MP4 + AV1 + AAC
video/webm; codecs="vp9, opus"WebM + VP9 + Opus
video/webm; codecs="av01.0.08M.08, opus"WebM + AV1 + Opus
audio/mpegMP3 / MPEG audio
audio/mp4; codecs="mp4a.40.2"MP4/M4A 容器里的 AAC-LC

注意:这些字符串不是“随便写给人看的注释”,而是浏览器、MSE、服务端响应头、播放器兼容性判断都会用到的东西。

前端代码里经常会这样判断:

const video = document.createElement("video");

const candidates = [
  'video/mp4; codecs="avc1.42E01E, mp4a.40.2"',
  'video/mp4; codecs="hvc1.1.6.L120.90, mp4a.40.2"',
  'video/webm; codecs="vp9, opus"',
  'video/webm; codecs="av01.0.08M.08, opus"',
  'audio/mpeg',
];

for (const type of candidates) {
  console.log(type, "=>", video.canPlayType(type));
}

可能输出:

video/mp4; codecs="avc1.42E01E, mp4a.40.2" => probably
video/mp4; codecs="hvc1.1.6.L120.90, mp4a.40.2" => ""
video/webm; codecs="vp9, opus" => probably
video/webm; codecs="av01.0.08M.08, opus" => maybe
audio/mpeg => probably

这个结果不是标准答案,不同浏览器、系统、硬件会不同。重点是:

不要只问:你支持 MP4 吗?
要问:你支持这个 container + codec + profile 吗?

11. 裸流是什么

裸流,也叫:

elementary stream
raw stream
bitstream

它指的是:

只有某种编码格式自己的连续数据,没有完整容器包装。

常见例子:

裸流说明
.h264H.264 Annex B 码流,常见 start code 是 00 00 00 01
.aac可能是 ADTS AAC,每帧带 ADTS header
.mp3MPEG Audio frames,加上可选 ID3 标签
.ivf简单视频码流容器,常用于 VP8/VP9 测试,不是业务常见发布格式
Raw PCM只有采样值,甚至可能没有采样率、声道数等头信息

裸流的问题是:

播放器不一定知道:
- 时长是多少
- 有几条轨
- 每帧时间戳是多少
- codec 参数在哪里
- 如何 seek

所以真实业务里更常见的是:

H.264 + AAC → MP4
VP9 + Opus → WebM
AAC → M4A / MP4
PCM → WAV

MP3 是个有点特殊的存在。它常以 .mp3 文件形式出现,但底层更像连续的 MPEG 音频帧流,元数据通过 ID3v2 放在开头、ID3v1 放在末尾等方式附加,而不是像 MP4 那样用复杂 box tree 管理多轨和索引。([IETF Datatracker][3])


12. 几组最容易混的概念

12.1 MP4 vs H.264

MP4:容器
H.264:视频 codec

正确说法:

这个 MP4 文件的视频轨是 H.264 编码。

错误说法:

这个视频是 MP4 编码。

更工程化的说法:

container = mp4
video codec = avc1 / H.264
audio codec = mp4a / AAC

12.2 MP3 vs AAC

MP3:音频 codec / MPEG 音频帧流
AAC:音频 codec

它们都是压缩音频的方法,不是“视频容器”。

常见文件:

song.mp3     → 通常是 MP3 音频帧 + ID3 标签
song.m4a     → 通常是 MP4/M4A 容器 + AAC 音频
audio.aac    → 可能是 ADTS AAC 裸流

一句话:

MP3 和 AAC 都是音频压缩格式;AAC 常被放进 MP4/M4A 容器里,MP3 则通常以连续 MPEG 音频帧加 ID3 元数据的方式存在。


12.3 WebM vs VP9 / Opus

WebM:容器
VP9:视频 codec
Opus:音频 codec

正确说法:

这个 WebM 文件里有 VP9 视频轨和 Opus 音频轨。

错误说法:

WebM 就是 VP9。

WebM 官方文档说明 WebM 是 Matroska 的子集,并且目标之一是适合 VP8/VP9 和开放 Web。(WebM Project)


12.4 解封装 vs 解码

这是面试高频混淆点。

demux:从容器里拆出编码数据
decode:把编码数据还原成原始帧 / PCM

对 MP4 来说:

MP4
  ↓ demux
H.264 samples + AAC samples
  ↓ decode
VideoFrame + PCM

所以:

拆 MP4 box ≠ 解码 H.264
解析 MP3 frame header ≠ 解码 MP3 音频
拿到 EncodedVideoChunk ≠ 拿到 VideoFrame

12.5 重新封装 vs 转码

这两个词在业务里非常常见。

Remux:重新封装

不改 codec,只换容器。

input.mkv
├── H.264 视频
└── AAC 音频

remux

output.mp4
├── H.264 视频
└── AAC 音频

特点:

速度快
画质不变
体积变化通常不大
不需要完整解码和重新编码

Transcode:转码

改 codec,需要解码再编码。

input.mp4
├── H.265 视频
└── AAC 音频

transcode

output.mp4
├── H.264 视频
└── AAC 音频

流程:

H.265 encoded video
  ↓ decode
raw VideoFrame
  ↓ encode
H.264 encoded video

特点:

速度慢
可能损失画质
CPU/GPU 消耗大
兼容性可变好

面试时可以这样说:

Remux 是换包装,不换内容;Transcode 是把内容拆开重新压缩。


13. 和真实前端工程的关系

13.1 上传文件预览

用户上传一个 .mp4,你不能只看后缀就认为浏览器一定能播。

更稳的判断方式是:

function canPlay(type: string): boolean {
  const video = document.createElement("video");
  const result = video.canPlayType(type);
  return result === "probably" || result === "maybe";
}

console.log(
  canPlay('video/mp4; codecs="avc1.42E01E, mp4a.40.2"')
);

但注意,canPlayType() 也不是 100% 保证播放成功,它只是浏览器基于 MIME 和 codec 信息做能力判断。MDN 对返回值的定义也只是 probablymaybe 或空字符串。(MDN Web Docs)


13.2 WebCodecs 处理 MP4 文件

很多新人会以为:

const file = input.files[0];
const buffer = await file.arrayBuffer();

const decoder = new VideoDecoder(...);
decoder.decode(buffer); // 错误心智模型

问题是:

VideoDecoder 不能直接吃完整 MP4 文件。

你需要:

File / ArrayBuffer
  ↓ MP4 demuxer
EncodedVideoChunk
  ↓ VideoDecoder
VideoFrame

WebCodecs 处理的是 codec 层数据,不是完整容器文件。MDN 对 WebCodecs 的解释也是围绕 raw frame/data 和 encoded chunk 的转换:VideoDecoder 把 EncodedVideoChunk 转成 VideoFrame,AudioDecoder 把 EncodedAudioChunk 转成 AudioData。(MDN Web Docs)


13.3 导出 MP4 文件

同样,很多新人会以为:

const chunks: EncodedVideoChunk[] = [];

const encoder = new VideoEncoder({
  output(chunk) {
    chunks.push(chunk);
  },
  error: console.error,
});

// 最后把 chunks 拼起来就是 mp4?不是。

EncodedVideoChunk 只是编码后的视频块,还不是一个标准 MP4 文件。

你还需要:

EncodedVideoChunk
+
EncodedAudioChunk
+
track metadata
+
timestamp
+
codec config
+
MP4 box 结构
  ↓ muxer
output.mp4

这就是为什么很多 WebCodecs 项目会搭配:

mp4box.js
mp4-muxer
webm-muxer
ffmpeg.wasm
自研最小 muxer

这一章先把概念拆清楚,后面到 WebCodecs 实战时,你就不会把 encoder output 和“可保存的视频文件”混为一谈。


14. 常见误区

误区 1:.mp4 文件一定是 H.264

不一定。

.mp4 只是容器后缀。里面的视频轨可能是:

H.264
H.265 / HEVC
AV1
MPEG-4 Part 2
甚至其他 codec

误区 2:浏览器支持 MP4,就支持所有 MP4

不一定。

浏览器可能支持:

MP4 + H.264 + AAC

但不支持:

MP4 + HEVC
MP4 + 10-bit H.264
MP4 + 某些 profile / level
MP4 + 不常见音频 codec

误区 3:demux 就是 decode

不是。

demux:拆容器
decode:解压缩 codec 数据

误区 4:编码后的视频 chunk 可以直接保存成 MP4

不行。

WebCodecs encoder 输出的是 codec chunk,不是容器文件。要保存成 MP4 / WebM,还要 mux。


误区 5:MP3 是和 MP4 一样的容器

不准确。

MP3 通常是 MPEG 音频帧流加元数据标签,不是 MP4 那种通用多轨容器。


误区 6:MIME type 只要写 video/mp4 就够

工程上通常不够。

更好的写法是:

video/mp4; codecs="avc1.640028, mp4a.40.2"

因为 video/mp4 只能说明容器,不能说明里面的 codec 和 profile。MDN 也明确说明 codecs 参数可以补充容器内部 codec、profile 等信息。(MDN Web Docs)


15. 必须掌握的术语表

术语中文一句话解释
Container容器组织音频、视频、字幕、元数据的文件格式
Codec编解码器 / 编码格式压缩和还原音视频数据的规则
Encoder编码器原始帧 / PCM → 编码数据
Decoder解码器编码数据 → 原始帧 / PCM
Muxer封装器多条编码轨道 → 容器文件
Demuxer解封装器容器文件 → 编码轨道数据
Track轨道容器中的一条媒体流,比如视频轨、音频轨
Sample样本容器层的一段媒体数据,常对应一帧或一段音频数据
Frame视频画面帧,或音频 codec frame,语境要看清
Bitstream码流编码后的连续二进制数据
Elementary Stream基本流 / 裸流没有完整容器包装的单一编码流
MIME type媒体类型Web 中描述资源类型的字符串
Codec string编码标识字符串MIME 里说明具体 codec/profile 的参数
Remux重新封装不改 codec,只换容器
Transcode转码解码后重新编码,改变 codec 或参数

16. 三个实践任务

实践 1:检测浏览器对常见容器 + codec 的支持

const types = [
  'video/mp4; codecs="avc1.42E01E, mp4a.40.2"',
  'video/mp4; codecs="avc1.640028, mp4a.40.2"',
  'video/mp4; codecs="hvc1.1.6.L120.90, mp4a.40.2"',
  'video/mp4; codecs="av01.0.08M.08, mp4a.40.2"',
  'video/webm; codecs="vp9, opus"',
  'video/webm; codecs="av01.0.08M.08, opus"',
  'audio/mpeg',
  'audio/mp4; codecs="mp4a.40.2"',
];

const video = document.createElement("video");

for (const type of types) {
  console.log(type, "=>", video.canPlayType(type));
}

你要观察:

Chrome / Edge / Safari / Firefox 输出有什么不同?
同一个浏览器在 Windows / macOS / iOS 上是否不同?
只写 video/mp4 和写完整 codecs 参数有什么不同?

实践 2:写一个格式分类器

目标:输入一个名字,输出它是容器、视频 codec、音频 codec,还是原始数据。

type MediaKind =
  | "container"
  | "video-codec"
  | "audio-codec"
  | "raw-audio"
  | "unknown";

const formatMap: Record<string, MediaKind> = {
  mp4: "container",
  webm: "container",
  mkv: "container",

  h264: "video-codec",
  avc: "video-codec",
  h265: "video-codec",
  hevc: "video-codec",
  av1: "video-codec",
  vp9: "video-codec",

  aac: "audio-codec",
  mp3: "audio-codec",
  opus: "audio-codec",
  vorbis: "audio-codec",

  pcm: "raw-audio",
};

function classifyFormat(name: string): MediaKind {
  return formatMap[name.toLowerCase()] ?? "unknown";
}

console.log(classifyFormat("mp4"));  // container
console.log(classifyFormat("h264")); // video-codec
console.log(classifyFormat("aac"));  // audio-codec
console.log(classifyFormat("pcm"));  // raw-audio

扩展任务:

给每个格式补上:
- 常见后缀
- 常见 MIME type
- 常见搭配
- 浏览器兼容性备注

实践 3:画出一个媒体处理 pipeline

把下面几个需求分别画成流程图:

需求 A:播放一个 MP4

MP4 file
  ↓ demux
H.264 samples + AAC samples
  ↓ decode
VideoFrame + PCM
  ↓ render / audio output
屏幕 + 扬声器

需求 B:MP4 转 WebM

MP4 file
  ↓ demux
H.264 + AAC
  ↓ decode
VideoFrame + PCM
  ↓ encode
VP9 + Opus
  ↓ mux
WebM file

需求 C:MKV 转 MP4,但不改变 codec

MKV file
  ↓ demux
H.264 + AAC
  ↓ mux
MP4 file

这个是 remux,不是 transcode。

需求 D:给视频加水印

MP4 file
  ↓ demux
H.264 samples
  ↓ decode
VideoFrame
  ↓ Canvas 绘制水印
新 VideoFrame
  ↓ encode
H.264 / VP9 / AV1 chunks
  ↓ mux
新 MP4 / WebM

只要涉及“改画面内容”,通常就绕不开:

decode → process → encode

17. 面试题与参考回答

题 1:MP4 和 H.264 有什么区别?

简洁回答:

MP4 是容器格式,H.264 是视频编码格式。MP4 负责组织音频、视频、字幕、时间戳和元数据;H.264 负责压缩视频画面。一个 MP4 文件里可以放 H.264 视频,也可以放 H.265、AV1 等其他视频编码。

深入回答:

播放一个 MP4 时,播放器会先 demux MP4 容器,读出 track 信息、sample table、时间戳和 codec config,然后把视频轨里的 H.264 samples 送给 H.264 decoder,解码成原始视频帧,再渲染到屏幕。所以容器层和 codec 层是两层不同职责。

踩坑回答:

MP4 就是 H.264。 H.264 是视频文件格式。 MP4 解码后就是 H.264。


题 2:浏览器支持 MP4,为什么有些 MP4 还是播不了?

参考回答:

因为 MP4 只是容器。浏览器支持 MP4 容器,不代表支持里面所有可能的 codec、profile、level、bit depth 和音频格式。例如浏览器可能支持 MP4 + H.264 + AAC,但不支持 MP4 + HEVC,或者不支持某些 10-bit profile。工程上应该用带 codecs 参数的 MIME type 做能力判断,比如 video/mp4; codecs="avc1.640028, mp4a.40.2"


题 3:muxing 和 encoding 有什么区别?

参考回答:

Encoding 是编码,把原始音视频数据压缩成 H.264、AAC、Opus 等编码数据。Muxing 是封装,把已经编码好的音频、视频、字幕、时间戳和元数据写进 MP4、WebM、MKV 等容器。编码改变数据压缩方式,封装改变文件组织方式。


题 4:demuxing 和 decoding 有什么区别?

参考回答:

Demuxing 是从容器里拆出编码后的音视频 sample,比如从 MP4 里拆出 H.264 samples 和 AAC samples。Decoding 是把这些编码数据真正解码成原始视频帧或 PCM 音频。demux 后的数据通常还是压缩的,不能直接画到屏幕或播放成声音。


题 5:WebCodecs 能不能直接读取 MP4?

参考回答:

WebCodecs 主要处理 codec 层的数据,比如 EncodedVideoChunk、EncodedAudioChunk、VideoFrame、AudioData。完整 MP4 是容器文件,里面有 box、track、sample table、mdat 等结构。要用 WebCodecs 解码 MP4,一般需要先用 demuxer 从 MP4 里拆出编码 samples,再构造成 EncodedVideoChunk 送给 VideoDecoder。


题 6:EncodedVideoChunk 拼起来是不是 MP4?

参考回答:

不是。EncodedVideoChunk 是编码后的视频块,不包含完整 MP4 容器所需的 box、track metadata、sample table、索引、音频轨等信息。要生成可播放 MP4,还需要 muxer 把编码后的 chunks 和元数据封装成 MP4 文件。


题 7:remux 和 transcode 有什么区别?

参考回答:

Remux 是重新封装,不改变 codec,比如 MKV 里的 H.264 + AAC 重新封装成 MP4。Transcode 是转码,需要先解码再重新编码,比如把 H.265 视频转成 H.264。Remux 通常快且无画质损失;transcode 更慢,可能有画质损失,但可以提升兼容性或改变码率、分辨率等参数。


18. 本章总结

这一章你要记住这条链路:

容器文件
  ↓ demux 解封装
编码数据 samples / chunks
  ↓ decode 解码
原始帧 VideoFrame / PCM / AudioData
  ↓ process 处理
新的原始帧 / 音频
  ↓ encode 编码
新的编码数据 chunks
  ↓ mux 封装
新的容器文件

再浓缩成四句话:

MP4 / WebM / MKV 是容器。
H.264 / H.265 / AV1 是视频 codec。
AAC / MP3 / Opus 是音频 codec。
WebCodecs 处理 codec 层,不负责完整容器的 mux / demux。

面试里最值钱的不是背出一堆格式名,而是能讲清楚:

文件后缀
容器格式
codec
profile / level
封装
解封装
编码
解码

它们分别在哪一层、解决什么问题。


19. 自测题

题 1

.mp4 文件一定是 H.264 编码吗?

答案:

不一定。MP4 是容器格式,里面的视频轨可以是 H.264,也可以是 H.265、AV1 或其他 codec。


题 2

从 MP4 中 demux 出来的 H.264 sample 可以直接画到 Canvas 吗?

答案:

不能。demux 出来的 H.264 sample 仍然是压缩编码数据,需要经过 H.264 decoder 解码成 VideoFrame 或像素数据后,才能绘制到 Canvas。


题 3

video/mp4; codecs="avc1.640028, mp4a.40.2"video/mp4 多表达了什么?

答案:

它不仅说明容器是 MP4,还说明视频 codec 是 H.264/AVC 的某种 profile/level,音频 codec 是 AAC-LC。这样浏览器能更准确判断是否支持播放。


题 4

input.mkv 里的 H.264 + AAC 不重新编码,直接写成 output.mp4,这叫什么?

答案:

叫 remux,重新封装。它改变容器,不改变音视频 codec。


题 5

把 H.265 视频转成 H.264 视频,这叫什么?

答案:

叫 transcode,转码。它需要先解码 H.265 得到原始视频帧,再用 H.264 encoder 重新编码。


题 6

WebCodecs 的 VideoEncoder 输出的 EncodedVideoChunk 可以直接保存成 .mp4 吗?

答案:

不可以。EncodedVideoChunk 是编码后的视频块,还不是 MP4 文件。要保存成 MP4,需要 muxer 生成 MP4 容器结构和相关元数据。


20. 下一章衔接

下一章开始进入 MP4 内部结构。

这一章我们只说:

MP4 是容器。

下一章要具体拆开看:

MP4 这个容器里面到底长什么样?

你会看到这些关键词:

ftyp
moov
mdat
trak
mdia
minf
stbl
stsd
stts
stsc
stsz
stco / co64
stss

也就是:

播放器到底怎么知道:
- 这个 MP4 有几条轨?
- 视频宽高是多少?
- 音频采样率是多少?
- 每一帧在文件哪里?
- 每一帧什么时候播放?
- seek 时该跳到哪里?

一句话预告:

Chapter 3 会把 MP4 从“一个文件”拆成一棵 box tree。

[3]: https://datatracker.ietf.org/doc/rfc3003/

    RFC 3003 - The audio/mpeg Media Type

    "

[7]: https://datatracker.ietf.org/doc/html/rfc6716

            RFC 6716 - Definition of the Opus Audio Codec

    "

[13]: https://datatracker.ietf.org/doc/html/rfc6381

            RFC 6381 - The 'Codecs' and 'Profiles' Parameters for \"Bucket\" Media Types

    "