WebRTC QoS方法十三.2(Jitter延时的计算)

一、背景介绍

一些报文在网络传输中,会存在丢包重传和延时的情况。渲染时需要进行适当缓存,等待丢失被重传的报文或者正在路上传输的报文。

jitter延时计算是确认需要缓存的时间

另外,在检测到帧有重传情况时,也可适当在渲染时间内增加RTT延时时间,等待丢失重传的报文

二、jitter实现原理

JitterDelay由两部分延迟造成:传输大帧引起的延迟和网络噪声引起的延迟。计算公式如下:

其中:

estimate[0]:信道传输速率的倒数
MaxFrameSize:表示自会话开始以来所收到的最大帧size
AvgFrameSize:表示平均帧大小,排除keyframe等超大帧

kNoiseStdDevs:       表示噪声系数2.33
var_noise_ms2_:     表示噪声方差
kNoiseStdDevOffset:  表示噪声扣除常数30 

实现函数:

JitterEstimator::CalculateEstimate

1、传输大帧引起的延迟

传输大帧引起的延迟

这个公式的原理是:[milliseconds] = [1 / bytes per millisecond] * [bytes] 

实现函数:

double FrameDelayVariationKalmanFilter::GetFrameDelayVariationEstimateSizeBased(double frame_size_variation_bytes) const {// Unit: [1 / bytes per millisecond] * [bytes] = [milliseconds].return estimate_[0] * frame_size_variation_bytes;
}

filtered_max_frame_size_bytes

=  std::max<double>(kPsi * max_frame_size_bytes_, frame_size.bytes());

constexpr double kPsi = 0.9999;

filtered_avg_frame_size_bytes

是每一帧的加权平均值,但是需要排除key frame这种超大帧

estimate_[0]参数计算

使用一个简化卡尔曼滤波算法,在处理帧延迟变化(frame_delay_variation_ms)的估计,考虑了帧大小变化(frame_size_variation_bytes)和最大帧大小(max_frame_size_bytes)作为输入参数。

void FrameDelayVariationKalmanFilter::PredictAndUpdate(double frame_delay_variation_ms,double frame_size_variation_bytes,double max_frame_size_bytes,double var_noise) {// Sanity checks.if (max_frame_size_bytes < 1) {return;}if (var_noise <= 0.0) {return;}// This member function follows the data flow in// https://en.wikipedia.org/wiki/Kalman_filter#Details.// 1) Estimate prediction: `x = F*x`.// For this model, there is no need to explicitly predict the estimate, since// the state transition matrix is the identity.// 2) Estimate covariance prediction: `P = F*P*F' + Q`.// Again, since the state transition matrix is the identity, this update// is performed by simply adding the process noise covariance.estimate_cov_[0][0] += process_noise_cov_diag_[0];estimate_cov_[1][1] += process_noise_cov_diag_[1];// 3) Innovation: `y = z - H*x`.// This is the part of the measurement that cannot be explained by the current// estimate.double innovation =frame_delay_variation_ms -GetFrameDelayVariationEstimateTotal(frame_size_variation_bytes);// 4) Innovation variance: `s = H*P*H' + r`.double estim_cov_times_obs[2];estim_cov_times_obs[0] =estimate_cov_[0][0] * frame_size_variation_bytes + estimate_cov_[0][1];estim_cov_times_obs[1] =estimate_cov_[1][0] * frame_size_variation_bytes + estimate_cov_[1][1];double observation_noise_stddev =(300.0 * exp(-fabs(frame_size_variation_bytes) /(1e0 * max_frame_size_bytes)) +1) *sqrt(var_noise);if (observation_noise_stddev < 1.0) {observation_noise_stddev = 1.0;}// TODO(brandtr): Shouldn't we add observation_noise_stddev^2 here? Otherwise,// the dimensional analysis fails.double innovation_var = frame_size_variation_bytes * estim_cov_times_obs[0] +estim_cov_times_obs[1] + observation_noise_stddev;if ((innovation_var < 1e-9 && innovation_var >= 0) ||(innovation_var > -1e-9 && innovation_var <= 0)) {RTC_DCHECK_NOTREACHED();return;}// 5) Optimal Kalman gain: `K = P*H'/s`.// How much to trust the model vs. how much to trust the measurement.double kalman_gain[2];kalman_gain[0] = estim_cov_times_obs[0] / innovation_var;kalman_gain[1] = estim_cov_times_obs[1] / innovation_var;// 6) Estimate update: `x = x + K*y`.// Optimally weight the new information in the innovation and add it to the// old estimate.estimate_[0] += kalman_gain[0] * innovation;estimate_[1] += kalman_gain[1] * innovation;// (This clamping is not part of the linear Kalman filter.)if (estimate_[0] < kMaxBandwidth) {estimate_[0] = kMaxBandwidth;}// 7) Estimate covariance update: `P = (I - K*H)*P`double t00 = estimate_cov_[0][0];double t01 = estimate_cov_[0][1];estimate_cov_[0][0] =(1 - kalman_gain[0] * frame_size_variation_bytes) * t00 -kalman_gain[0] * estimate_cov_[1][0];estimate_cov_[0][1] =(1 - kalman_gain[0] * frame_size_variation_bytes) * t01 -kalman_gain[0] * estimate_cov_[1][1];estimate_cov_[1][0] = estimate_cov_[1][0] * (1 - kalman_gain[1]) -kalman_gain[1] * frame_size_variation_bytes * t00;estimate_cov_[1][1] = estimate_cov_[1][1] * (1 - kalman_gain[1]) -kalman_gain[1] * frame_size_variation_bytes * t01;// Covariance matrix, must be positive semi-definite.RTC_DCHECK(estimate_cov_[0][0] + estimate_cov_[1][1] >= 0 &&estimate_cov_[0][0] * estimate_cov_[1][1] -estimate_cov_[0][1] * estimate_cov_[1][0] >=0 &&estimate_cov_[0][0] >= 0);
}

2、网络噪声引起的延迟

网络噪声引起的延迟

constexpr double kNoiseStdDevs = 2.33; //噪声系数

constexpr double kNoiseStdDevOffset = 30.0;//噪声扣除常数

var_noise_ms2_ //噪声方差

实现函数:

噪声方差var_noise_ms2计算

var_noise_ms2 = alpha * var_noise_ms2_ + 
                (1 - alpha) *(d_dT - avg_noise_ms_) *(d_dT - avg_noise_ms_);

实现函数:JitterEstimator::EstimateRandomJitter

其中:

d_dT = 实际FrameDelay - 评估FrameDelay

             在JitterEstimator::UpdateEstimate函数实现

             

实际FrameDelay = (两帧之间实际接收gap - 两帧之间实际发送gap)

             在InterFrameDelayVariationCalculator::Calculate函数实现

absl::optional<TimeDelta> InterFrameDelayVariationCalculator::Calculate(uint32_t rtp_timestamp,Timestamp now) {int64_t rtp_timestamp_unwrapped = unwrapper_.Unwrap(rtp_timestamp);if (!prev_wall_clock_) {prev_wall_clock_ = now;prev_rtp_timestamp_unwrapped_ = rtp_timestamp_unwrapped;// Inter-frame delay variation is undefined for a single frame.// TODO(brandtr): Should this return absl::nullopt instead?return TimeDelta::Zero();}// Account for reordering in jitter variance estimate in the future?// Note that this also captures incomplete frames which are grabbed for// decoding after a later frame has been complete, i.e. real packet losses.uint32_t cropped_prev = static_cast<uint32_t>(prev_rtp_timestamp_unwrapped_);if (rtp_timestamp_unwrapped < prev_rtp_timestamp_unwrapped_ ||!IsNewerTimestamp(rtp_timestamp, cropped_prev)) {return absl::nullopt;}// Compute the compensated timestamp difference.TimeDelta delta_wall = now - *prev_wall_clock_;int64_t d_rtp_ticks = rtp_timestamp_unwrapped - prev_rtp_timestamp_unwrapped_;TimeDelta delta_rtp = d_rtp_ticks / k90kHz;// The inter-frame delay variation is the second order difference between the// RTP and wall clocks of the two frames, or in other words, the first order// difference between `delta_rtp` and `delta_wall`.TimeDelta inter_frame_delay_variation = delta_wall - delta_rtp;prev_wall_clock_ = now;prev_rtp_timestamp_unwrapped_ = rtp_timestamp_unwrapped;return inter_frame_delay_variation;
}

评估FrameDelay =  estimate[0] * (FrameSize – PreFrameSize) + estimate[1]

评估FrameDelay实现函数:

double FrameDelayVariationKalmanFilter::GetFrameDelayVariationEstimateTotal(double frame_size_variation_bytes) const {double frame_transmission_delay_ms =GetFrameDelayVariationEstimateSizeBased(frame_size_variation_bytes);double link_queuing_delay_ms = estimate_[1];return frame_transmission_delay_ms + link_queuing_delay_ms;
}

3、jitter延时更新流程

三、RTT延时计算 

VideoStreamBufferController::OnFrameReady函数,在判断帧有重传情况时,还会根据实际情况,在渲染帧时间里面增加RTT值。

JitterEstimator::GetJitterEstimate根据实际配置,可以在渲染时间中适当增加一定比例的RTT延时值。 

 

四、参考

WebRTC视频接收缓冲区基于KalmanFilter的延迟模型 - 简书在WebRTC的视频处理流水线中,接收端缓冲区JitterBuffer是关键的组成部分:它负责RTP数据包乱序重排和组帧,RTP丢包重传,请求重传关键帧,估算缓冲区延迟等功能...icon-default.png?t=N7T8https://www.jianshu.com/p/bb34995c549a

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/49243.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【目标检测实验系列】EMA高效注意力机制,融合多尺度特征,助力YOLOv5检测模型涨点(文内附源码)

1. 文章主要内容 本篇博客主要涉及多尺度高效注意力机制&#xff0c;融合到YOLOv5s模型中&#xff0c;增加模型提取多尺度特征的能力&#xff0c;助力模型涨点。&#xff08;通读本篇博客需要7分钟左右的时间&#xff09;。 2. 简要概括 论文地址&#xff1a;EMA论文地址 如下…

Blender材质-PBR与纹理材质

1.PBR PBR:Physically Based Rendering 基于物理的渲染 BRDF:Bidirection Reflectance Distribution Function 双向散射分散函数 材质着色操作如下图&#xff1a; 2.纹理材质 左上角&#xff1a;编辑器类型中选择&#xff0c;着色器编辑器 新建着色器 -> 新建纹理 -> 新…

音视频入门基础:H.264专题(17)——FFmpeg源码获取H.264裸流文件信息(视频压缩编码格式、色彩格式、视频分辨率、帧率)的总流程

音视频入门基础&#xff1a;H.264专题系列文章&#xff1a; 音视频入门基础&#xff1a;H.264专题&#xff08;1&#xff09;——H.264官方文档下载 音视频入门基础&#xff1a;H.264专题&#xff08;2&#xff09;——使用FFmpeg命令生成H.264裸流文件 音视频入门基础&…

【开源库编译 | zlib】 zlib库最新版本(zlib-1.3.1)在Ubuntu(Linux)系统下的 编译 、交叉编译(移植)

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

《书生大模型实战营第3期》入门岛 学习笔记与作业:Git 基础知识

文章大纲 Git 是什么&#xff1f;-- 分布式版本控制系统版本控制系统简介Git 基本概念1. 安装 Git1.1 Windows 系统1.2 Linux 系统 2. Git 托管平台3. 常用 Git 操作4. tips4.1 全局设置 vs. 本地设置4.2 如何配置4.3 验证设置4.4 Git 四步曲 5. 常用插件6. 常规开发流程 作业其…

js+css侧边导航菜单 可收缩

jscss侧边导航菜单 可收缩https://www.bootstrapmb.com/item/14774 创建一个可收缩的侧边导航菜单需要使用JavaScript来处理交互&#xff0c;而CSS则用来设置样式和动画效果。以下是一个简单的示例&#xff0c;展示了如何创建一个可收缩的侧边导航菜单。 HTML 结构 html<!…

重修之路1

我也不知道我现在处于个什么状态&#xff0c;我在以前写代码时知道部分方法如何使用&#xff0c;但是也仅限于此我并不了其如何实现&#xff0c;让我感到迷茫我是越来越菜了随着AI的发展它写出的代码简洁高效甚至让我有些看不懂&#xff0c;以至于我开始怀疑自己的JS基本功因此…

【接口测试】params传参与body传参区别

文章目录 一.params传参二.body传参三.两者区别说明 一.params传参 params传参一般用于get请求 params传参时,参数会附于URL后面以问号形式展示。 示例&#xff1a; http://ip地址:端口号/login?usernamexm&pwd111二.body传参 body传参一般用于post请求 body传参时需…

MacOS安装SDKMan管理Java版本

文章目录 1 简介2 安装与卸载2.1 安装2.2 卸载 3 使用3.1 查看其他工具&#xff1a;支持 Ant, Maven 等3.2 查看Java版本3.3 安装Java&#xff0c;加上相关的版本3.4 设置Java版本(全局)3.5 只在当前窗口生效3.6 卸载1 默认环境无法卸载 4 jdk安装的位置5 与IDEA集成参考 1 简介…

简单修改,让UE4/5着色器编译速度变快

简单修改&#xff0c;让UE4/5着色器编译速度变快 目录 简单修改&#xff0c;让UE4/5着色器编译速度变快 一、问题描述 二、解决方法 &#xff08;一&#xff09;硬件升级 &#xff08;二&#xff09;调整相关设置和提升优先级 1.调整相关设置 &#xff08;1&#xff09…

Pytorch 6

罗切斯特回归模型 加了激活函数 加了激活函数之后类 class LogisticRegressionModel(torch.nn.Module):def __init__(self):super(LogisticRegressionModel, self).__init__()self.linear torch.nn.Linear(1,1)def forward(self, x):# y_pred F.sigmoid(self.linear(x))y_p…

Java学习 - Spring Boot整合 Thymeleaf 实例

什么是 Thymeleaf Thymeleaf 是新一代的 Java 模板引擎&#xff0c;类似于 Velocity、FreeMarker 等传统引擎&#xff0c;其语言和 HTML 很接近&#xff0c;而且扩展性更高&#xff1b; Thymeleaf 的主要目的是将优雅的模板引入开发工作流程中&#xff0c;并将 HTML 在浏览器中…

MSPM0G3507学习笔记1:开发环境_引脚认识与点灯

今日速通一款Ti的单片机用于电赛&#xff1a;MSPM0G3507 这里默认已经安装好了Keil5_MDK 首先声明一下: 因为是速成&#xff0c;所以需要一定单片机学习基础&#xff0c;然后我写的也不会详细&#xff0c;这个专栏的笔记也就是自己能看懂就行的目标~~~ 文章提供测试代码解…

智能制造·数字化工厂建设规划方案(65P)

获取完整PPT见下图 更多有关华为研发管理/IPD、MBSE、PLM、ERP、MES、数据治理、数字样机等方面免费解决方案、资料获取&#xff0c;请见下图

Ecovadis评估的流程是什么

Ecovadis评估流程是一个全面、系统且注重细节的过程&#xff0c;旨在为企业提供关于其可持续性表现的深入洞察。这一评估不仅覆盖了企业在环境、社会和治理方面的多个方面&#xff0c;还强调了持续改进的重要性&#xff0c;确保企业能够不断提升其CSR&#xff08;企业社会责任&…

ES中聚合查询之date_histogram查询出现key_as_string 和 key含义

ES中聚合查询之date_histogram查询出现key_as_string 和 key含义 DSL语句 #实例 GET /capture_features_202407/_search {"query": {"bool": {"must": [{"terms": {"plateNo": ["汉A00001"]}},{"range&quo…

构建一个具有深色模式的简单React Web应用

在当今的Web开发世界里,创建一个既美观又功能丰富的用户界面是至关重要的。在本文中,我们将探讨如何使用React构建一个简单但功能强大的Web应用,它包含导航栏、内容展示区域和深色模式切换功能。 项目概述 我们的目标是创建一个具有以下特性的Web应用: 左侧导航栏,包含四个链…

Qt创建自定义组件并且promote to之后导致编译错误(CMake)

创建自定组件并且加入到全局(勾选"Global include"选项)后&#xff0c;重新编译&#xff0c;元对象编译器生成的ui_xxxx.h文件中会新加入自定义组件的头文件&#xff1a; 如图所示&#xff0c;编译器提示找不到自定义组件的头文件&#xff1a; Solution: 在CMakeL…

opencascade AIS_InteractiveContext源码学习9 obsolete methods

AIS_InteractiveContext 前言 交互上下文&#xff08;Interactive Context&#xff09;允许您在一个或多个视图器中管理交互对象的图形行为和选择。类方法使这一操作非常透明。需要记住的是&#xff0c;对于已经被交互上下文识别的交互对象&#xff0c;必须使用上下文方法进行…

Leetcode3216. 交换后字典序最小的字符串

Every day a Leetcode 题目来源&#xff1a;3216. 交换后字典序最小的字符串 解法1&#xff1a;模拟 找到第一个 s[i] > s[i 1]&#xff0c;且它们奇偶性相同&#xff0c;交换它们。 代码&#xff1a; /** lc appleetcode.cn id3216 langcpp** [3216] 交换后字典序最小…