RTCRemoteOutboundRtpStreamStats: localId 属性

RTCRemoteOutboundRtpStreamStats 字典的 localId 属性是一个字符串,可用于标识 RTCInboundRtpStreamStats 对象,其 remoteId 与此值匹配。

这两个对象一起提供了关于同一同步源 (SSRC) 的入站和出站方面的统计信息。

一个字符串,可以与 RTCInboundRtpStreamStats 对象的 remoteId 属性的值进行比较,以查看两者是否代表本地对等体接收的同一组数据的两个方面的统计信息。

使用说明

您可以将同一 RTP 流的本地和远程视图视为对,每一对都包含对另一方的引用。因此,如果一个 RTCStatsReport 包含一个 remote-outbound-rtp 统计信息对象(类型为 RTCRemoteOutboundRtpStreamStats),则它还应包含一个相应的 inbound-rtp 对象。这两个对象都提供了有关从远程对等体到本地设备传输的相同数据包批次的信息。

不同之处在于 remote-outbound-rtp 描述了从远程对等体的角度来看远程对等体发送数据的统计信息,而 inbound-rtp 提供了从本地对等体的角度来看传入数据的统计信息。

您可以在 Glitch 上检查、尝试和试验 此示例。

示例

在此示例中,我们有一对函数:第一个函数 networkTestStart() 捕获初始报告,第二个函数 networkTestStop() 捕获第二个报告。第二个函数使用这两个报告输出有关网络状况的一些信息。

networkTestStart()

此函数调用 RTCPeerConnection 方法 getStats() 请求一个 RTCStatsReport 并将其存储在变量 startReport 中。

js
let startReport;

async function networkTestStart(pc) {
  if (pc) {
    startReport = await pc.getStats();
  }
}

给定一个 RTCPeerConnectionpc,这将调用其 getStats() 方法以获取统计信息报告对象,并将其存储在 startReport 中,以便在 networkTestStop() 收集测试结束数据后使用。

networkTestStop()

networkTestStop() 函数获取第二个报告 endReport,然后计算并输出结果。

查找配对统计信息

每个类型为 remote-outbound-rtp 的统计信息记录(描述远程对等体发送到本地对等体的统计信息)都对应一个类型为 inbound-rtp 的记录,该记录描述了本地对等体对这两个对等体之间移动的相同数据的看法。让我们创建一个实用函数来帮助我们查找配对统计信息对象中密钥的值。

下面显示的 findReportEntry() 函数检查一个 RTCStatsReport,返回包含指定 keyRTCStatsReport 基统计信息记录——并且密钥具有指定 value。如果未找到匹配项或统计信息报告没有与 key 指示的统计信息类别相对应的记录。

js
function findReportEntry(report, key, value) {
  for (const stats of report.values()) {
    if (stats[key] === value) {
      return stats;
    }
  }
  return null;
}

由于 RTCStatsReport 是一个 JavaScript Map,我们可以迭代地图的 values() 来检查报告中的每个 RTCStats 基统计信息记录,直到我们找到一个具有指定 valuekey 属性的记录。找到匹配项后,将返回统计信息对象。

如果未找到匹配项,则该函数返回 null

主要的 networkTestStop() 函数

现在让我们看看 networkTestStop() 函数本身。它以正在测试的 RTCPeerConnection 作为输入,调用 getStats() 获取具有当前统计信息的新的 RTCStatsReport,然后计算它要查找的结果,通过将适当的 HTML 附加到类为 stats-box<div> 元素的内容来以适当的方式向用户输出这些结果。

js
async function networkTestStop(pc) {
  if (pc) {
    const statsBox = document.querySelector(".stats-box");
    const endReport = await pc.getStats();

    for (const endRemoteOutbound of endReport.values()) {
      if (endRemoteOutbound.type === "remote-outbound-rtp") {
        const startRemoteOutbound = startReport.get(endRemoteOutbound.id);

        if (startRemoteOutbound) {
          const startInboundStats = findReportEntry(
            startReport,
            "remoteId",
            startRemoteOutbound.id,
          );
          const endInboundStats = findReportEntry(
            endReport,
            "remoteId",
            endRemoteOutbound.id,
          );
          // Elapsed time in seconds
          const elapsedTime =
            (endRemoteOutbound.timestamp - startRemoteOutbound.timestamp) /
            1000;
          const packetsSent =
            endRemoteOutbound.packetsSent - startRemoteOutbound.packetsSent;
          const bytesSent =
            endRemoteOutbound.bytesSent - startRemoteOutbound.bytesSent;
          const framesDecoded =
            endInboundStats.framesDecoded - startInboundStats.framesDecoded;
          const frameRate = framesDecoded / elapsedTime;

          let timeString = "";
          if (!isNaN(elapsedTime)) {
            timeString = ` representing ${elapsedTime}s`;
          }

          let frameString = "";
          if (!isNaN(framesDecoded)) {
            frameString = `Decoded ${framesDecoded} frames for a frame rate of ${frameRate.toFixed(
              2,
            )} FPS.<br>`;
          }

          const logEntry =
            `<div class="stats-entry"><h2>Report ID: ${endRemoteOutbound.id}</h2>` +
            `Remote peer sent ${packetsSent} packets ${timeString}.<br>` +
            `${frameString}` +
            `Data size: ${bytesSent} bytes.</div>`;
          statsBox.innerHTML += logEntry;
        } else {
          statsBox.innerHTML += `<div class="stats-error">Unable to find initial statistics for ID ${endRemoteOutbound.id}.</div>`;
        }
      }

      statsBox.scrollTo(0, statsBox.scrollHeight);
    }
  }
}

以下是在 networkTestStop() 函数中进行的操作:在调用 RTCPeerConnection 方法 getStats() 获取连接的最新统计信息报告并将其存储在 endReport 中之后。这是一个 RTCStatsReport 对象,它将字符串映射到对应 RTCStatsReport 基类型的对象。

现在我们可以开始处理结果,从 endReport 中找到的结束统计信息开始。在这种情况下,我们正在寻找 typeremote-outbound-rtp 的统计信息记录,因此我们迭代统计信息报告中的条目,直到我们找到一个类型为 remote-outbound-rtp 的条目。此对象具体来说是类型为 RTCRemoteOutboundRtpStreamStats,它提供了统计信息,详细介绍了事物从远程对等体的角度来看的状态。此统计信息记录存储在 endRemoteOutbound 中。

找到结束的 remote-outbound-rtp 记录后,我们使用其 id 属性获取其 ID。有了它,我们可以在起始统计信息记录(startReport)中查找 remote-outbound-rtp 记录,将其存储到 startRemoteOutbound 中。

现在我们通过查找它们中 remoteId 属性的值为 remote-outbound-rtp 记录的 ID 来获取与这两个 remote-outbound-rtp 记录相对应的 inbound-rtp 统计信息。我们使用上一节中描述的 findReportEntry() 函数来实现这一点,并将定位到的 inbound-rtp 记录存储在 startInboundStatsendInboundStats 中。

现在我们拥有了计算我们要显示的信息所需的所有原始统计信息,因此我们这样做。

  • 我们通过从 endReporttimestamp 中减去 startReporttimestamp 来计算两个报告之间经过的时间——elapsedTime。然后我们除以 1000 将结果从毫秒转换为秒。
  • 我们通过从两个报告的 packetsSent 属性的值中减去来计算在此间隔内发送的数据包数量——packetsSent
  • 类似地,在此间隔内发送的字节数——bytesSent——是通过从起始统计信息对象的 bytesSent 属性中减去结束统计信息的属性来计算的。
  • 在此间隔内解码的帧数——framesDecoded——是通过从 startRecordframesDecoded 中减去 endRecord.framesDecoded 来确定的。
  • 最后,通过将 framesDecoded 除以 elapsedTime 来计算经过时间跨度上的帧速率。

networkTestStop() 函数的其余部分构造用于向用户显示收集和计算结果输出的 HTML,然后将其附加到元素 statsBox,我们使用它来向用户显示状态更新。

给定示例使用的样式,输出日志如下所示

A screenshot of the example showing logged statistics taken from paired remote-outbound-rtp and inbound-rtp statistics records

在屏幕截图中,我们看到一个标题,后面跟着我们称为 statsBox 的可滚动 <div>。该框包含许多日志条目,其中最后几个可见。每个条目大约代表一秒钟的时间(因为这是我们在调用 networkTestStart()networkTestStop() 之间等待的时间)。

尝试和分叉

此示例可在 Glitch 上供您尝试、检查或重混。

重混它

查看源代码

规范

规范
WebRTC 统计信息 API 的标识符
# dom-rtcremoteoutboundrtpstreamstats-localid

浏览器兼容性

BCD 表格仅在启用了 JavaScript 的浏览器中加载。