# getStats 与 RTCStatsReport · 标准化的指标快照

> getStats 返回的 RTCStatsReport 是一份 W3C 标准化的指标快照——一堆带 id/type/timestamp 的 RTCStats 字典。这一章读 stats/ 源码，讲清这个数据模型、几个最有用的标准 stats 类型（inbound/outbound-rtp、candidate-pair、transport），以及怎么用它们读出通话质量。

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

# getStats 与 RTCStatsReport · 标准化的指标快照

[上一章](https://daiw.org/manual/webrtc/observability-overview)说 [`getStats` 是“快照”](https://daiw.org/manual/webrtc/observability-overview)。这一章看这份快照的**结构**——它不是随便一堆数字，而是一套 **W3C 标准化**的数据模型。理解它，你就能从任何 WebRTC 应用（甚至 `chrome://webrtc-internals`）读出通话质量。

## RTCStatsReport：一份 id → 指标 的字典集合

`getStats()` 返回一个 `RTCStatsReport`。看它的接口（真实源码 `stats/rtc_stats_report.h`）：

```cpp
// stats/rtc_stats_report.h(节选,真实源码)
class RTCStatsReport final {
  static scoped_refptr<RTCStatsReport> Create(Timestamp timestamp);
  Timestamp timestamp() const;                 // 这份快照的时刻
  void AddStats(std::unique_ptr<const RTCStats> stats);
  const RTCStats* Get(const std::string& id) const;   // 按 id 查
  ConstIterator begin() const;                 // 可遍历所有 stats
  std::string ToJson() const;                  // 导出 JSON
};
```

<Callout type="info">
  **`RTCStatsReport` 是“**某一刻整通电话的所有指标**”的集合——内部是一堆 [`RTCStats` 对象](https://daiw.org/manual/webrtc/stats-report)，每个有唯一 `id`，可按 id 查、可遍历、可 `ToJson` 导出。它是[引用计数的](https://daiw.org/manual/webrtc/scoped-refptr)（`scoped_refptr`），带一个统一的 `timestamp`（整份快照的时刻）。** 而每个 `RTCStats` 是一条“某个对象的指标记录”（真实源码 `stats/rtc_stats.h`）：

  ```cpp
  class RTCStats {
    const std::string& id() const;      // 唯一标识,如 "RTCInboundRTPVideoStream_..."
    Timestamp timestamp() const;        // 这条记录的时刻
    virtual const char* type() const;   // 类型名,如 "inbound-rtp"
    std::vector<Attribute> Attributes() const;   // 这条记录的所有字段
  };
  ```

  于是数据模型是**两层**：一份报告（`RTCStatsReport`）装着许多条记录（`RTCStats`），每条记录有 `id`/`type`/`timestamp` 和一组具体字段。这个结构[W3C 标准化](https://daiw.org/manual/webrtc/observability-overview)了——所有浏览器的 `getStats()` 都返回这个形状，字段名也标准，所以监控代码可跨浏览器复用。
</Callout>

## 几个最有用的标准类型

`type()` 决定这条记录讲的是“哪个对象”。源码 `stats/rtcstats_objects.h` 定义了全部标准类型（[对应 W3C 规范](https://daiw.org/manual/webrtc/observability-overview)），下面几个是排障时最常看的：

| type | 类（源码） | 讲什么 | 关键字段 |
| --- | --- | --- | --- |
| `inbound-rtp` | `RTCInboundRtpStreamStats` | **收到**的一路流 | `packetsLost`、`jitter`、`framesDecoded`、`frameWidth/Height` |
| `outbound-rtp` | `RTCOutboundRtpStreamStats` | **发出**的一路流 | `bytesSent`、`framesEncoded`、`targetBitrate`、`qualityLimitationReason` |
| `remote-inbound-rtp` | `RTCRemoteInboundRtpStreamStats` | 对端**收我**的情况（经 [RTCP](https://daiw.org/manual/webrtc/rtcp-reports)） | `roundTripTime`、`packetsLost`、`jitter` |
| `candidate-pair` | `RTCIceCandidatePairStats` | 选中的 [ICE 路径](https://daiw.org/manual/webrtc/connection-and-checks) | `currentRoundTripTime`、`availableOutgoingBitrate`、`state` |
| `transport` | `RTCTransportStats` | [DTLS/传输](https://daiw.org/manual/webrtc/dtls-handshake)层 | `bytesSent/Received`、`dtlsState`、`selectedCandidatePairId` |
| `codec` | `RTCCodecStats` | 用了哪个[编解码器](https://daiw.org/manual/webrtc/payload-types) | `mimeType`、`clockRate`、`payloadType` |

<Callout type="info">
  **这几个类型就能拼出一通电话的“体检报告”：**

  - **画质 / 音质怎么样** —— `inbound-rtp` 的 [`framesDecoded`、`frameWidth/Height`](https://daiw.org/manual/webrtc/video-timing)、`jitter`（[解码帧率、分辨率、抖动](https://daiw.org/manual/webrtc/video-jitter-buffer)）。
  - **丢包多不多** —— `inbound-rtp.packetsLost` / `remote-inbound-rtp.packetsLost`（[收发两侧的丢包](https://daiw.org/manual/webrtc/nack-rtx)）。
  - **延迟多大** —— `candidate-pair.currentRoundTripTime` / `remote-inbound-rtp.roundTripTime`（[RTT](https://daiw.org/manual/webrtc/rtcp-reports)）。
  - **带宽够不够** —— `outbound-rtp.targetBitrate`（[拥塞控制给的目标码率](https://daiw.org/manual/webrtc/rate-control)）、`candidate-pair.availableOutgoingBitrate`。
  - **为什么画质被限** —— `outbound-rtp.qualityLimitationReason`（`cpu`/`bandwidth`/`none`——[是 CPU 顶不住还是带宽不够](https://daiw.org/manual/webrtc/video-adaptation)，直接告诉你）。
  - **走的什么路径** —— `transport` + `candidate-pair` 看是 [直连还是 TURN 中继](https://daiw.org/manual/webrtc/turn)、[DTLS 是否连上](https://daiw.org/manual/webrtc/dtls-handshake)。

  这些字段名不是 WebRTC 私有的，而是 [W3C 标准](https://daiw.org/manual/webrtc/observability-overview)——你在 `chrome://webrtc-internals` 里看到的、在任何 WebRTC 监控 SaaS 里看到的，都是它们。**读懂这张表，就读懂了绝大多数 WebRTC 质量监控。**
</Callout>

## stats 之间靠 id 互相引用

一个精妙处：各条 `RTCStats` 通过 **id 互相引用**，拼成一张关系图。例如 `outbound-rtp` 有个 `codecId` 字段，指向某条 `codec` 记录的 `id`；`candidate-pair` 有 `localCandidateId`/`remoteCandidateId` 指向 [ICE candidate](https://daiw.org/manual/webrtc/candidates) 记录；`transport` 有 `selectedCandidatePairId`。于是从“[我发的这路视频](https://daiw.org/manual/webrtc/rtp-sender)”能顺着 id 查到“[它用什么编解码器](https://daiw.org/manual/webrtc/payload-types)、走哪条 [ICE 路径](https://daiw.org/manual/webrtc/p2p-transport-channel)、经哪个 [DTLS 传输](https://daiw.org/manual/webrtc/dtls-handshake)”。`RTCStatsReport::Get(id)` 就是用来跟着这些引用跳转的。这张 **id 关系图**让快照不只是孤立数字，而是能还原“[谁连着谁](https://daiw.org/manual/webrtc/jsep-transport-controller)”的结构。

## 小结与下一章

`getStats()` 返回 **`RTCStatsReport`**——一份 [W3C 标准化](https://daiw.org/manual/webrtc/observability-overview)的指标快照：内部是许多条 **`RTCStats`**，每条有 `id`/`type`/`timestamp` 和一组标准字段，可按 id 查、遍历、`ToJson`。最有用的类型：`inbound-rtp`/`outbound-rtp`（[收发流的帧率、丢包、目标码率、`qualityLimitationReason`](https://daiw.org/manual/webrtc/video-adaptation)）、`remote-inbound-rtp`（[RTT](https://daiw.org/manual/webrtc/rtcp-reports)）、`candidate-pair`（[ICE 路径、RTT、可用带宽](https://daiw.org/manual/webrtc/connection-and-checks)）、`transport`、`codec`。各条 stats 通过 **id 互相引用**成关系图。读懂这套，就读懂了 `chrome://webrtc-internals` 和大多数质量监控。

这些指标[从各子系统怎么汇集](https://daiw.org/manual/webrtc/stats-collection)成一份报告？下一章看收集机制。👉 [统计的收集](https://daiw.org/manual/webrtc/stats-collection)。

## 参考资料

- [1] libwebrtc 源码.`stats/rtc_stats_report.h`、`stats/rtc_stats.h`、`stats/rtcstats_objects.h`（全部标准类型）。
- [2] W3C「Identifiers for WebRTC's Statistics API」（标准字段定义）。
- [3] 关联[第 1 部分统计收集器](https://daiw.org/manual/webrtc/stats-collector)、[RTCP 报告](https://daiw.org/manual/webrtc/rtcp-reports)。
