音频格式之AC3

AC3(Audio Coding 3),杜比第三代音频编码格式,也称杜比数字。

  • 声道配置:支持单声道、立体声、5.1 环绕声(左 / 右 / 中置 / 左后 / 右后 + 低音炮 LFE)
  • 码率范围:32kbit/s ~ 640kbit/s,DVD / 蓝光常用 384k、448k
  • 文件后缀:.ac3 原始音频流

EAC3 = Enhanced AC3(DD+ 杜比数字加) AC3 增强升级版,标准 A/52 Annex E,2005 年标准化,裸流后缀 .ec3, EAC3讲另外介绍,本文主要介绍AC3格式。

AC3文件格式:

该图很详细,引用:原创桓泽学音频编解码(2):AC3/Dolby Digital 系统算法分析-CSDN博客

一、SI信息(固定5字节)

cpp 复制代码
SI 帧头 (5字节)
       │
       ├─► syncword (2字节)  ──► 验证帧开始
       │
       ├─► CRC1 (2字节)         ──► CRC校验字节
       │
       ├─► buf[4] (1字节)    ──┬─► fscod(2位)  → 采样率
       │                       └─► frmsizecod(6位) → 帧大小码
      

1.1 同步字节

AC-3 帧以固定同步字 0x0B77 开头,这是识别帧边界的关键。

cpp 复制代码
if ((buf[0] != 0x0b) || (buf[1] != 0x77))  // syncword
    return 0;

1.2 CRC1

该 16 位 CRC 用于前 5/8 的同步帧数据段的校验。CRC 校验码采用大端模式传输。

1.3 采样率计算

帧大小计算公式由 AC-3 标准定义,与采样率和比特率相关。

fscode:主要用于采用率的计算;

cpp 复制代码
位布局: | fscod[1:0] |  frmsizecod[5:0]   |
           7  6         5  4  3  2  1  0

switch (buf[4] & 0xc0) {
case 0:      // 48kHz
    *sample_rate = 48000 >> half;
    return 4 * bitrate;
case 0x40:   // 44.1kHz
    *sample_rate = 44100 >> half;
    return 2 * (320 * bitrate / 147 + (frmsizecod & 1));
case 0x80:   // 32kHz
    *sample_rate = 32000 >> half;
    return 6 * bitrate;
}

1.3 比特率计算

AC-3 支持 19 种标准比特率(32kbps ~ 640kbps),通过 frmsizecod 查表获取。

cpp 复制代码
位布局: | fscod[1:0] |  frmsizecod[5:0]   |
           7  6         5  4  3  2  1  0

static int rate[] = {32, 40, 48, 56, 64, 80, 96, 112,
                     128, 160, 192, 224, 256, 320, 384, 448,
                     512, 576, 640};
frmsizecod = buf[4] & 63;
bitrate = rate[frmsizecod >> 1];
*bit_rate = (bitrate * 1000) >> half;

frmsizecod: 共 6 位,高 5 位用于索引比特率表,最低位用于帧大小计算。在标准文档中有一张表直接映射关系。

二、BSI信息(可变)

cpp 复制代码
┌───────────────────────────────────────────────────────── BSI 整体 ─────────────────────────────────────────────────┐
│ 固定必读基础段(无条件全部存在)                                                                                   │
│  ├─ bsid        5bit                                                                                              │
│  ├─ bsmod       3bit                                                                                              │
│  ├─ acmod       3bit                                                                                              │
│                                                                                                                    │
│      条件分支1:if ((acmod & 0x1) && acmod != 0x1) 存在三前置LCR声道                                                   │
│          ├─ [cmixlev]   2bit                                                                                              │                                                                                                                   
│      条件分支2:if (acmod & 0x4) 存在环绕声道                                                                          │
│          ├─ [surmixlev] 2bit                                                                                                                                                                                                                  │
│      条件分支3:if (acmod == 0x2) 纯3.0 LCR模式                                                                         │
│          ├─ [dsurmod]   2bit                                                                                              │
│                                                                                                                    │
│  继续固定必读段                                                                                                    │
│  ├─ lfeon       1bit                                                                                              │
│  ├─ dialnorm    5bit                                                                                              │
│  ├─ compre      1bit                                                                                              │
│  │  └─ 条件分支4:if(compre=1)                                                                                     │
│  │     └─ [compr] 8bit                                                                                            │
│  ├─ langcode    1bit                                                                                              │
│  │  └─ 条件分支5:if(langcode=1)                                                                                   │
│  │     └─ [langcod] 8bit                                                                                          │
│  ├─ audprodie   1bit                                                                                              │
│  │  └─ 条件分支6:if(audprodie=1)                                                                                 │
│  │     ├─ [mixlevel] 5bit                                                                                         │
│  │     └─ [roomtyp]  2bit                                                                                         │
│                                                                                                                    │
│  条件分支7:if (acmod == 0) 双单声道 1+1 专属段                                                                    │
│  ├─ [dialnorm2]    5bit                                                                                           │
│  ├─ [compr2e]      1bit                                                                                           │
│  │  └─ if(compr2e=1) → [compr2] 8bit                                                                               │
│  ├─ [langcod2e]    1bit                                                                                           │
│  │  └─ if(langcod2e=1) → [langcod2] 8bit                                                                           │
│  ├─ [audprodi2e]   1bit                                                                                           │
│  │  └─ if(audprodi2e=1)                                                                                           │
│  │     ├─ [mixlevel2] 5bit                                                                                        │
│  │     └─ [roomtyp2]  2bit                                                                                        │
│                                                                                                                    │
│  继续固定必读段                                                                                                    │
│  ├─ copyrightb  1bit                                                                                              │
│  ├─ origbs      1bit                                                                                              │
│  ├─ timecod1e   1bit                                                                                              │
│  │  └─ if(timecod1e=1) → [timecod1] 14bit                                                                          │
│  ├─ timecod2e   1bit                                                                                              │
│  │  └─ if(timecod2e=1) → [timecod2] 14bit                                                                          │
│  ├─ addbsie     1bit                                                                                              │
│  │  └─ 条件分支8:if(addbsie=1)                                                                                   │
│  │     ├─ addbsil 6bit                                                                                            │
│  │     └─ addbsi  (addbsil+1)×8 bit (动态长度 ?)                                                                │
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
               

2.1 比特流版本检查

bsid: buf[5] 的高 5 位是 bsid(比特流识别码),对于AC3,值 >= 12 表示不支持的版本;EAC3支持>=12的值。

除非该码流按照AC3标准任一附录规范构建,否则该比特字段(bsid)取值应为01000(十进制 8)。若读取到bsid 大于 8 ,应静音输出(解码器若额外兼容EAC3除外); 若读取到bsid 小于等于 8,则应对音频进行解码并正常播放。

半采样率处理:

某些 AC-3 流使用半采样率(如 24kHz),需根据 bsid 确定降采样因子。

cpp 复制代码
static uint8_t halfrate[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3};
half = halfrate[buf[5] >> 3];

bsid 还可以用来区分是AC3还是EAC3,

|----------|------------|-----------|
| bsid | half 值 | 实际采样率 |
| 0-8 | 0 | 标准采样率 |
| 9 | 1 | 标准/2 |
| 10 | 2 | 标准/4 |
| 11 | 3 | 标准/8 |

2.2 比特率模式

cpp 复制代码
bsmod = buf[5] & 7

2.3 声道配置解

  • acmod:声道模式(单声道/立体声/5.1等)
    A52_DOLBY:杜比环绕声特殊标识
    A52_LFE:是否包含低频效果声道
cpp 复制代码
acmod = buf[6] >> 5;  // 音频模式(前3位)
*flags = ((((buf[6] & 0xf8) == 0x50) ? A52_DOLBY : acmod) |
          ((buf[6] & lfeon[acmod]) ? A52_LFE : 0));

2.3.1 cmixlev 中置混音增益

当解码器仅输出双声道立体声时,控制中置 (C) 声道混入左右前置声道的衰减幅度,避免人声过重 / 过轻。

cmixlev 值 衰减量 用途说明
00 0.707 (--3.0 dB) 中置人声混入立体声音量更大,对白突出
01 0.595 (--4.5 dB) DVD / 影视通用标准值,均衡自然
10 0.500 (--6.0 dB) 压低中置人声,前置左右声道为主
11 保留 解码器默认按 - 4.5dB 处理

2.3.2 surmixlev: Surround Mix Level 环绕混声增益

多声道音轨向下兼容立体声 2.0 输出时,控制环绕声道(LS/RS/S) 混入左右主声道的衰减量,平衡前后声场音量。

2.3.3 dsurmod: Dolby Surround Mode 杜比混声增益

标记当前 2.0 母带立体声是否经过杜比模拟矩阵编码(Dolby Surround / Pro Logic / Pro Logic II),给后端功放 / 解码器提示:是否启用矩阵解码,从双声道分离出中置、后置环绕声道。 AC3 数字解码器本身不依赖此字段运算,主要供 AV 功放、播放设备的环绕扩展逻辑使用。

2.4 lfeon (Low Frequency Effects Channel on)低频效果声道启用标志

  • lfeon=1:解码时分配独立低频声道缓冲区,输出信号到低音炮通道;
  • lfeon=0:不存在 LFE 数据,解码器不生成低频轨道;
  • 若播放设备无低音炮,解码器会把 LFE 低频分量按规则混入左右前置声道。
acmod 主声道布局 lfeon=0 lfeon=1
001(1) LR 立体声 2.0 2.1
010(2) LCR 三前置 3.0 3.1
011(3) LCR+LS RS 4.0 4.1
100(4) LCRS 4.0 4.1
101(5) L C R LS RS 5.0 5.1(标准影院配置)

2.5 dialnorm: Dialogue Normalization,对白归一化

记录影片人声对白的平均响度基准,用于统一不同片源播放音量,解决一部电影音量大、另一部音量小的问题

对白基准响度(单位 dBFS): Dialogue Level=−4−(dialnorm×2)

其它属性,本文章不接着详细介绍,具体可以参考标准文档,

三、audblk: Audio Block

单音频块语法层级结构图, 一帧有固定的6个AudioBlock,需要循环解码

cpp 复制代码
┌──────────────────────────────────────────────── audblk() 单个AudioBlock ──────────────────────────────────────────────┐
│ 1. 块切换与抖动标志(所有声道必读)
│ ├─ for ch 0~nfchans-1: blksw[ch] 1bit       // MDCT长短块切换标志
│ └─ for ch 0~nfchans-1: dithflag[ch] 1bit    // 量化抖动开关
│
│ 2. 动态范围控制
│ ├─ dynrnge 1bit
│ │  └─ [dynrnge=1] → dynrng 8bit  // 主声道动态压缩参数
│ └─ [acmod == 0 双单声道]
│    ├─ dynrng2e 1bit
│    └─ [dynrng2e=1] → dynrng2 8bit // 第二单声道独立动态参数
│
│ 3. 声道耦合策略(高频多声道合并压缩核心)
│ ├─ cplstre 1bit // 是否存在耦合数据
│ │ └─ [cplstre=1]
│ │    ├─ cplinu 1bit // 本块启用耦合
│ │    │ └─ [cplinu=1]
│ │    │  ├─ for ch 0~nfchans-1: chincpl[ch] 1bit // 该声道参与耦合
│ │    │  ├─ [acmod == 2] → phsflginu 1bit // 相位标志启用
│ │    │  ├─ cplbegf 4bit 耦合起始频带
│ │    │  ├─ cplendf 4bit 耦合结束频带
│ │    │  └─ for bnd 1~ncplsubnd-1: cplbndstrc[bnd] 1bit // 耦合子带结构
│
│ 4. 耦合系数、相位标志(仅开启耦合cplinu=1)
│ └─ [cplinu=1]
│    ├─ 遍历所有声道 ch
│    │  └─ [chincpl[ch]=1 该声道参与耦合]
│    │     ├─ cplcoe[ch] 1bit // 该声道存在耦合系数
│    │     │ └─ [cplcoe[ch]=1]
│    │     │  ├─ mstrcplco[ch] 2bit // 耦合主系数模式
│    │     │  └─ for bnd 0~ncplbnd-1: cplcoexp[ch][bnd]4bit + cplcomant[ch][bnd]4bit
│    └─ [acmod==2 && phsflginu && (cplcoe[0]||cplcoe[1])]
│       └─ for bnd 0~ncplbnd-1: phsflg[bnd] 1bit // 立体声耦合相位标记
│
│ 5. 立体声重矩阵 rematrix(仅acmod=2 三前置LCR声道)
│ └─ [acmod == 2]
│    ├─ rematstr 1bit // 是否开启重矩阵
│    │ └─ [rematstr=1]
│    │  ├─ cplbegf>2 或 cplinu=0 → for rbnd0~3 rematflg[rbnd]1bit
│    │  ├─ 0<cplbegf≤2 && cplinu=1 → for rbnd0~2 rematflg[rbnd]1bit
│    │  └─ cplbegf==0 && cplinu=1 → for rbnd0~1 rematflg[rbnd]1bit
│
│ 6. 指数编码策略(频带幅度粗量化模式)
│ ├─ [cplinu=1] → cplexpstr 2bit // 耦合声道指数策略
│ ├─ for ch 0~nfchans-1: chexpstr[ch] 2bit // 各独立声道指数策略
│ ├─ [lfeon=1] → lfeexpstr 1bit // 低音LFE指数策略
│ └─ 遍历所有声道 ch
│    └─ [chexpstr[ch] != reuse 不复用前块指数]
│       └─ [chincpl[ch]==0 非耦合声道] → chbwcod[ch]6bit // 声道带宽编码
│
│ 7. 指数数据 exponents(频带幅度粗值)
│ ├─ [cplinu=1 耦合声道指数区]
│ │  └─ [cplexpstr != reuse]
│ │     ├─ cplabsexp 4bit
│ │     └─ for grp0~ncplgrps-1: cplexps[grp]7bit
│ ├─ 遍历全频声道 ch
│ │  └─ [chexpstr[ch] != reuse]
│ │     ├─ exps[ch][0] 4bit
│ │     ├─ for grp1~nchgrps[ch]: exps[ch][grp]7bit
│ │     └─ gainrng[ch] 2bit
│ └─ [lfeon=1 LFE低音指数]
│    └─ [lfeexpstr != reuse]
│       ├─ lfeexps[0]4bit
│       └─ for grp1~2: lfeexps[grp]7bit
│
│ 8. 比特分配心理声学参数(bit allocation 核心)
│ ├─ baie 1bit // 是否存在比特分配扩展参数
│ │ └─ [baie=1]
│ │    ├─ sdcycod 2bit, fdcycod 2bit
│ │    ├─ sgaincod 2bit, dbpbcod 2bit, floorcod 3bit
│ ├─ snroffste 1bit // 信噪比偏移启用
│ │ └─ [snroffste=1]
│ │    ├─ csnroffst 6bit
│ │    ├─ [cplinu=1] → cplfsnroffst4bit + cplfgaincod3bit
│ │    ├─ for ch: fsnroffst[ch]4bit, fgaincod[ch]3bit
│ │    └─ [lfeon=1] → lfefsnroffst4bit, lfefgaincod3bit
│ └─ [cplinu=1]
│    ├─ cplleake 1bit
│    └─ [cplleake=1] → cplfleak3bit, cplsleak3bit
│
│ 9. 差分比特分配 delta bit allocation
│ ├─ deltbaie 1bit // 差分分配参数启用
│ │ └─ [deltbaie=1]
│ │    ├─ [cplinu=1] → cpldeltbae 2bit
│ │    ├─ for ch: deltbae[ch] 2bit
│ │    ├─ [cpldeltbae==新分段]
│ │    │  ├─ cpldeltnseg3bit
│ │    │  └─ for seg0~cpldeltnseg: cpldeltoffst5 / cpldeltlen4 / cpldeltba3
│ │    └─ 遍历声道 ch
│ │       └─ [deltbae[ch]==新分段]
│ │          ├─ deltnseg[ch]3bit
│ │          └─ for seg0~deltnseg[ch]: deltoffst5 / deltlen4 / deltba3
│
│ 10. 填充跳过数据 dummy skip 填充位
│ ├─ skiple 1bit
│ │ └─ [skiple=1]
│ │    ├─ skipl 9bit
│ │    └─ skipfld 长度=skipl×8 bit 空白占位比特
│
│ 11. 量化尾数 mantissas 音频原始频域系数(音频核心载荷)
│ ├─ got_cplchan=0 标记位
│ ├─ 遍历所有声道 ch
│ │  ├─ for bin0~nchmant[ch]-1: chmant[ch][bin] 变长0~16bit
│ │  └─ [cplinu && chincpl[ch] && !got_cplchan]
│ │     ├─ for bin0~ncplmant-1: cplmant[bin] 0~16bit
│ │     └─ got_cplchan=1 避免重复读取耦合尾数
│ └─ [lfeon=1 低音声道尾数]
│    └─ for bin0~nlfemant-1: lfemant[bin] 0~16bit
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘


注:
数字:字段占用比特
[]:条件分支,判断成立才读取
循环 for():多声道 / 多频带重复读取
变长 (0--16):mantissa 尾数动态比特宽度
nfchans:有效全频声道数量,由 BSI acmod 推导

3.1 块切换与抖动标志(blksw /dithflag)

  • blksw(Block Switch)长短 MDCT 切换
    • 0:长块(512 点 MDCT),适合平稳人声、背景音乐,频率分辨率高;
    • 1:短块(256 点 MDCT),适合冲击音(爆炸、敲击),抑制时域混叠失真。 每个声道可独立选择长短块。
  • dithflag 量化抖动开关 1 = 开启抖动,低音量细微信号增加随机噪声,消除量化底噪失真;0 = 关闭,节省码率。

3.2. 动态范围控制(dynrnge /dynrng/dynrng2)

针对单块独立动态范围调节(区别 BSI 全局 compr):

  1. dynrnge=1 时,8bit dynrng 定义本块强弱音量压缩曲线,爆炸、轰鸣自动压低峰值,小声对白放大;
  2. acmod=0 双单声道 1+1,左右两路独立音频,dynrng2 给第二声道单独动态参数;
  3. 仅作用当前 audblk,逐块动态调整,比 BSI 全局压缩更精细。

3.3. 声道耦合策略 cplstre /cplinu/chincpl (AC3 核心码率压缩手段)

人耳对高频方位不敏感,把多个声道高于某频点的高频信号合并为一条公共耦合声道编码,大幅节省比特。

  1. cplstre:1 = 本块存在耦合相关数据;0 = 无耦合,跳过全部耦合逻辑;
  2. cplinu:1 = 本块启用声道耦合;
  3. chincpl ch:标记该声道是否参与高频耦合;
  4. cplbegf /cplendf:耦合起始、终止频带号,低于 cplbegf 的低频独立编码,高于则合并;
  5. cplbndstrc:耦合子带划分标记,拆分耦合区间用于量化;
  6. phsflginu(仅 acmod=2 三声道):立体声耦合相位标志启用开关,用于还原左右声场相位。

3.4 耦合系数与相位标志 cplcoe /mstrcplco/phsflg

  • cplcoe:该参与耦合的声道是否需要独立耦合系数;
  • mstrcplco:耦合系数计算模式(主声道基准缩放);
  • cplcoexp + cplcomant:每个耦合子带的幅度指数、幅度尾数,描述该声道相对于公共耦合声道的增益比例;
  • phsflg(仅 acmod=2):耦合高频相位标记,解码时恢复立体声左右分离度,避免高频单声道模糊。

3.5 立体声重矩阵 rematrix(仅 acmod=2 LCR 三前置声道)

rematstr=1 开启重矩阵,rematflg 按频带标记是否启用矩阵解耦: 将混在左右声道里的中置人声、环绕信息重新分离还原,修复声场分离度; 根据耦合起始频带 cplbegf 不同,读取不同数量频带标记。

3,6 指数编码策略 expstr /chbwcod

exponent(指数)代表每个频带信号幅度粗量化值,是频域数据的音量基准。 策略分为 4 种模式:全部复用、分组复用、新指数、差值编码,用来减少指数占用比特。

  1. cplexpstr:耦合声道指数复用策略;
  2. chexpstr ch:每个独立全频声道指数策略;
  3. lfeexpstr:LFE 低音声道指数策略;
  4. chexpstr != reuse(不复用上一块指数)且声道不参与耦合时,读取 chbwcod 声道带宽编码:定义该声道有效最高频带,高于该频带无音频数据,不用编码,节约码率。

3.7 指数数据 exponents(频带幅度粗值)

耦合声道指数、全频声道指数、LFE 低音指数

指数是频域系数的缩放基准:频域幅值 = mantissa 尾数 × 2^exponent

  1. cplabsexp:耦合声道首组基准指数;cplexps:各组差分指数;
  2. exps ch0:独立声道首频带基准指数,后续分组差分存储;gainrng:本声道动态增益范围修正;
  3. LFE 低音独立一套指数,低频单独量化。 指数只存储粗略音量,精细波形由后续 mantissa 尾数表示。

3.8 心理声学基础比特分配参数 baie /snroffst/cplleak

AC3 心理声学核心模块,决定每个频带分配多少量化比特

3.8.1 baie 基础噪声遮蔽参数(baie=1 才存在)

sdcycod /fdcycod/sgaincod /dbpbcod/floorcod 控制人耳掩蔽曲线、高低频衰减、噪声基底,编码器据此计算每个频带允许的噪声,分配对应比特。

3.8.2 snroffste 信噪比偏移

csnroffst、fsnroffst ch、lfefsnroffst:全局 / 各声道信噪比偏移,微调掩蔽阈值; cplfgaincod/fgaincod:耦合 / 独立声道增益修正。

3.8.3 cplleake 耦合泄漏补偿

耦合高频合并后会有声场泄漏,cplfleak/cplsleak 补偿左右声道串音,恢复声场定位。

3.9 差分比特分配 deltbaie

在基础比特分配之上做频带局部修正

安静频带减少比特、复杂大动态频带增加比特; 用分段区间记录局部比特增减量,相比全局分配更精细,进一步压缩码率。 耦合声道与各独立声道分别存储差分修正分段。

3.10 填充跳过数据 skiple /skipl/skipfld

本 audblk 内部控制位、指数、比特分配、尾数总比特不足标准块长度时,插入空白占位比特补齐; 仅 skiple=1 存在,解码直接跳过该段,不参与音频计算。

3.11 量化尾数 mantissas 音频核心载荷

整个 audblk 真正承载音频波形的部分。

  1. 指数给出频带音量基准,mantissa 是该频带精细波形量化值;
  2. 比特数由前面心理声学 bit allocation 算出,静音频带分配 0bit 直接跳过;
  3. 读取逻辑:先读所有独立声道尾数,遇到参与耦合声道且未读取耦合尾数时读取公共耦合尾数(got_cplchan 标记避免重复读取);
  4. lfeon=1 时单独读取低音声道尾数; 解码后:mantissa × 2^exponent 还原完整 MDCT 频域系数,再逆 MDCT 得到时域 PCM 音频。

四,auxdata

该模块主要有两个作用:1,用于对齐帧长, 2,存放私有附加信息。

cpp 复制代码
┌──────────────────── auxdata() 辅助数据段 ────────────────────┐
│ 1. auxbits :nauxbits 比特长度(动态可变填充位)
│ 2. auxdatae 1bit :辅助扩展数据存在标志
│    └─ [auxdatae == 1]
│       └─ Auxdatal 14bit :辅助扩展数据长度
└─────────────────────────────────────────────────────────────┘
  • auxbits nauxbits 前置所有帧段后剩余空闲比特,用于补齐 AC3 帧固定总长度,无固定比特宽,由帧总长减去前面 SI+BSI+6×audblk 的总比特得到。
  • auxdatae 1bit 1 比特标志位,标记是否存在厂商自定义扩展辅助数据。
  • Auxdatal 14bit(条件存在) 仅 auxdatae=1 时读取,14bit 存储后续自定义 auxdata 字节长度。

五,errorcheck()

校验音频主体数据完整性,

  1. 解码器解码前重新计算 CRC,与码流内 crc2 比对:
  • 匹配:音频数据无损坏,正常解码播放;
  • 不匹配:传输 / 文件损坏,丢弃当前整帧,静音并重新搜索同步字。

2,格式介绍

cpp 复制代码
┌──────────── errorcheck() 帧尾校验区域 ───────────┐
│ crcrsv   1bit   保留校验位(预留未使用)
│ crc2     16bit  整帧音频数据CRC校验码
└──────────────────────────────────────────────────┘
  • crcrsv:1bit 保留位,标准预留,无实际校验作用,解码直接跳过;
  • crc2:16 位循环冗余校验,覆盖本帧 BSI + 6 个 audblk + auxdata 全部音频载荷,用于检测码流传输损坏。

3,区分两段CRC

  • crc1(SI 内):只校验 SI 后半段 + BSI;
  • crc2(errorcheck):校验音频主体数据块,双重校验提升容错。