阅读 204

WebRTC视频解码度量指标:帧间隔(InterframeDelay)计算原理

注:本文代码基于WebRTC 4472(对应Chromium 91),不保证适用于所有WebRTC版本。

在Chrome的 webrtc-internals 里可以看到很多关于媒体流的度量指标。目前Chrome(我用的版本是94)保留了传统(legacy)和WebRTC标准(standard)两种体系的度量数据。其中legacy的数据已经不再更新,也许未来某个时间就会从Chrome中消失了。无论legacy还是standard,对我们日常分析客户端侧的音视频质量数据还是非常重要的。

度量数据众多,本文只介绍度量指标中的一个:视频解码帧间隔。在legacy的webrtc-internals中存在一个帧间隔最大值,显示为googInterframeDelayMax。这个度量指标在standard中目前应该是不存在了,取而代之的是另外2个指标:totalInterFrameDelaytotalSquaredInterFrameDelay

让我们来看看这三个指标来自哪里。ReceiveStatisticsProxy通过上层 getStats 调用我们很容易就找到了这些度量值全部来自于 ReceiveStatisticsProxy这个类 (video\receive_statistics_proxy.cc)。其中,最大帧间隔来自于该类的成员变量 interframe_delay_max_moving_的Max方法。

我们先来分析一下 interframe_delay_max_moving_ :

mutable rtc::MovingMaxCounter<int> interframe_delay_max_moving_ RTC_GUARDED_BY(mutex_);复制代码

它的类型是rtc::MovingMaxCounter,实现文件:rtc_base\numerics\moving_max_counter.h,它的实现比较简单,就4个public方法:构造函数、Add、Max、Reset。其中最终要的是 Add 和 Max 这两个方法。

rtc::MovingMaxCounter维护着一个双向队列 std::deque。通过查看它的实现,Add会将小于待添加元素的末尾元素执行pop_back,全部删除掉。例如,假设队列中有199、64、55三个数值,再添加一个124,因为队列末尾的55、64小于124,执行Add后,队列中就变为199,124,而55和64将被删除。

rtc::MovingMaxCounter的Max方法,则按照构造函数传入的范围(或者叫滑动窗口),先删除不在范围内的元素(实现在RollWindow函数里),然后直接将队首的值返回(因为按照Add的实现方法,就保证了队首是最大值)。窗口默认值定义为 kMovingMaxWindowMs(1000),单位是ms。即只保留1000ms内的数值。

下图是RollWindow方法的一个示意图,画的比较潦草。RollWindow就是当添加新的元素⑥时,会根据设置的window长度(默认1000ms),将1000ms以前的队列元素全部删掉。

OK,再回到 ReceiveStatisticsProxy。 在ReceiveStatisticsProxy::OnDecodedFrame中我们看到,每解码一个视频帧出来,会用当前时间减去上一次解码时间,得到两帧之间的时间间隔:

void ReceiveStatisticsProxy::OnDecodedFrame(const VideoFrame& frame,
                                            absl::optional<uint8_t> qp,
                                            int32_t decode_time_ms,
                                            VideoContentType content_type) {
  MutexLock lock(&mutex_);

  uint64_t now_ms = clock_->TimeInMilliseconds();
  
  ......此处省略一些代码
  
  if (last_decoded_frame_time_ms_) {
    int64_t interframe_delay_ms = now_ms - *last_decoded_frame_time_ms_;
    RTC_DCHECK_GE(interframe_delay_ms, 0);
    double interframe_delay = interframe_delay_ms / 1000.0;
    stats_.total_inter_frame_delay += interframe_delay;
    stats_.total_squared_inter_frame_delay +=
        interframe_delay * interframe_delay;
    interframe_delay_max_moving_.Add(interframe_delay_ms, now_ms);
    content_specific_stats->interframe_delay_counter.Add(interframe_delay_ms);
    content_specific_stats->interframe_delay_percentiles.Add(
        interframe_delay_ms);
    content_specific_stats->flow_duration_ms += interframe_delay_ms;
  }
  if (stats_.frames_decoded == 1) {
    first_decoded_frame_time_ms_.emplace(now_ms);
  }
  last_decoded_frame_time_ms_.emplace(now_ms);
}复制代码

可以看到,每解码一帧出来,会计算两帧间隔时间interframe_delay_ms,添加到interframe_delay_max_moving_,另外÷1000.0换算成秒(interframe_delay),累加到total_inter_frame_delay,而interframe_delay的平方,累加到total_squared_inter_frame_delay

最后看一下 ReceiveStatisticsProxy::GetStats(),这个是给外面返回度量指标的方法,其中,最大帧间隔来自于:

stats_.interframe_delay_max_ms =
      interframe_delay_max_moving_.Max(now_ms).value_or(-1);复制代码

通过我们上面对 rtc::MovingMaxCounter的说明,可以得知,最大帧间隔就是过去1000ms内,解码帧间隔的最大值。

OK,最后总结一下:

googInterframeDelayMax :过去1000ms内,解码帧间隔的最大值,单位为毫秒
totalInterFrameDelay : 解码帧间隔的累加值,单位为秒
totalSquaredInterFrameDelay : 每次解码帧间隔平方的累加值,单位为秒


作者:深蓝色系统
链接:https://juejin.cn/post/7029251361225064455

文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐