# ACM 音频编码模块 · 编解码的统管者

> ACM（Audio Coding Module）是音频编解码的统管层，连接上层的流对象和底层的编解码器与 NetEq。这一章讲清 ACM 在发送侧驱动编码、在接收侧把包送进 NetEq 的角色，以及它作为「音频编解码门面」的定位。

- 作者：David（道雾轩）
- 专栏：音视频与 WebRTC 详解（https://daiw.org/manual/webrtc.md）
- 最后更新：2026-07-14
- 原文：https://daiw.org/manual/webrtc/acm
- 转载与引用：请注明出处并附原文链接（https://daiw.org/about/copyright）

# ACM 音频编码模块 · 编解码的统管者

[编解码器](https://daiw.org/manual/webrtc/audio-codecs)有了，谁来驱动它们、并把它们接到[收发流](https://daiw.org/manual/webrtc/audio-pipeline-overview)？是 **ACM（Audio Coding Module，`modules/audio_coding/`）**。它是音频编解码的**统管门面**，虽不像 [NetEq](https://daiw.org/manual/webrtc/neteq-overview)、[AEC3](https://daiw.org/manual/webrtc/aec3-internals) 那样有硬核算法，却是把音频编解码各部件粘合起来的关键。

## ACM 的两副面孔

ACM 在发送和接收两侧各有职责：

<Callout type="info">
  **发送侧，ACM 驱动[编码器](https://daiw.org/manual/webrtc/audio-codecs)把采集的音频压缩成包；接收侧，ACM 把收到的包送进 [NetEq](https://daiw.org/manual/webrtc/neteq-overview)，再取出解码后的音频。**

  - **发送侧（编码）** —— [APM 处理](https://daiw.org/manual/webrtc/apm-overview)干净的音频交给 ACM，ACM 用协商好的[编解码器（Opus）](https://daiw.org/manual/webrtc/audio-codecs)编码，产出压缩帧交给[打包发送](https://daiw.org/manual/webrtc/rtp-packet)。它还管[编码器的参数](https://daiw.org/manual/webrtc/audio-codecs)（码率、是否开 [FEC/DTX](https://daiw.org/manual/webrtc/audio-codecs)）——[拥塞控制的目标码率](https://daiw.org/manual/webrtc/rate-control)经它落到 Opus。
  - **接收侧（解码）** —— 收到的[音频包](https://daiw.org/manual/webrtc/rtp-demuxer)交给 ACM，ACM 把它们**喂进 [NetEq](https://daiw.org/manual/webrtc/neteq-overview)**（`InsertPacket`）；播放线程要音频时，ACM 从 NetEq **取出**（[`GetAudio`](https://daiw.org/manual/webrtc/neteq-overview)）处理好的音频。**NetEq 就住在 ACM 的接收侧内部**——ACM 是 NetEq 的宿主和门面。

  所以 ACM 是“[编解码器](https://daiw.org/manual/webrtc/audio-codecs) + [NetEq](https://daiw.org/manual/webrtc/neteq-overview)”的**封装层**：上层的 [`ChannelSend`/`ChannelReceive`](https://daiw.org/manual/webrtc/audio-pipeline-overview) 只跟 ACM 打交道，不必直接摆弄编解码器和 NetEq 的细节。
</Callout>

## 为什么需要这层门面

直接让[流对象](https://daiw.org/manual/webrtc/audio-pipeline-overview)操作[编解码器](https://daiw.org/manual/webrtc/audio-codecs)和 [NetEq](https://daiw.org/manual/webrtc/neteq-overview) 不行吗？ACM 这层门面的价值：

- **隐藏复杂度** —— [NetEq](https://daiw.org/manual/webrtc/neteq-overview) 内部极其复杂（[抖动缓冲、决策、PLC](https://daiw.org/manual/webrtc/neteq-operations)），ACM 把它包成简单的“塞包 / 取音频”两个动作，上层无需懂 NetEq 内幕。
- **统一编解码器管理** —— 一次通话可能[协商多个编解码器](https://daiw.org/manual/webrtc/media-session)、中途还可能切换，ACM 统一管理“当前用哪个编解码器解这个包”（[payload type](https://daiw.org/manual/webrtc/payload-types) → 编解码器的映射）。
- **线程边界** —— 编码在[发送相关的线程](https://daiw.org/manual/webrtc/threading-model)、解码 / NetEq 在[播放线程](https://daiw.org/manual/webrtc/threading-model)（被声卡的取音频回调驱动），ACM 协调这个边界。

这又是 WebRTC 一贯的[门面 / 抽象](https://daiw.org/manual/webrtc/source-map)手法：用一个中间层，把下面一堆复杂部件收拢成简洁接口。

## 在管线里的位置

```mermaid
flowchart TD
    subgraph SEND["发送侧"]
        APM["APM 处理后的音频"] --> ACMS["ACM:驱动 Opus 编码"]
        ACMS --> PK["打包 → 发送"]
    end
    subgraph RECV["接收侧"]
        RX["收到音频包"] --> ACMR["ACM"]
        ACMR -->|"InsertPacket"| NEQ["NetEq(住在 ACM 内)"]
        SND["声卡要音频"] -->|"GetAudio"| ACMR
        ACMR --> NEQ
        NEQ --> DEC["解码 + 抖动处理"]
        DEC --> OUT["播放"]
    end
```

ACM 是音频编解码的**中枢**：发送侧它是编码器的驱动，接收侧它是 [NetEq](https://daiw.org/manual/webrtc/neteq-overview) 的宿主。

## 小结与下一章

**ACM（音频编码模块）** 是音频编解码的**统管门面**：发送侧**驱动 [Opus 编码](https://daiw.org/manual/webrtc/audio-codecs)**（并管[码率 / FEC/DTX](https://daiw.org/manual/webrtc/audio-codecs) 参数）；接收侧**把包喂进 [NetEq](https://daiw.org/manual/webrtc/neteq-overview)**（`InsertPacket`）、**取出处理好的音频**（`GetAudio`）——**NetEq 就住在 ACM 接收侧内部**。这层门面隐藏了 NetEq 的复杂、统一管理[多编解码器](https://daiw.org/manual/webrtc/media-session)、协调[线程边界](https://daiw.org/manual/webrtc/threading-model)。

现在进入音频接收侧的明珠——**NetEq**。它把抖动缓冲、丢包隐藏、时间伸缩融为一体，是实时音频最精妙的模块。下一章总览。👉 [NetEq 总览](https://daiw.org/manual/webrtc/neteq-overview)。

## 参考资料

- [1] libwebrtc 源码.`modules/audio_coding/acm2/`（`AudioCodingModule`）、`api/audio_codecs/`。
- [2] libwebrtc 源码.`modules/audio_coding/neteq/g3doc/index.md`。
- [3] 关联[音频管线总览](https://daiw.org/manual/webrtc/audio-pipeline-overview)。
