feat(render): VSync 呈现时钟升级为去抖 PI 滤波,消除周期性 hitch#56
Conversation
将 CalculatePresentTime 从"朴素首帧锚点 + 漂移硬重置"改为 alpha-beta(PI)时钟恢复 + 自适应 cushion + 迟到帧不拽网格: - offset 小比例项(Kp=1/64)跟踪偏移均值,保持呈现网格近似刚性 - skew 积分项(Ki=1/2048)消除主机/客户端时钟频差的斜坡滞后 - 在线抖动估计驱动自适应 cushion(净 LAN 低延迟、抖动网络更深缓冲) - 迟到帧按原网格时刻立即呈现,网格保持连续,消除旧硬重置的周期性 hitch 仅影响 VSync 模式(config.enableVsync=true);低延迟模式走 OH_VideoDecoder_RenderOutputBuffer 直渲染,完全不受影响。 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
Warning Review limit reached
Next review available in: 54 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthrough本次修改重构了 NativeRender 的 VSync 呈现时间计算逻辑,改为基于单调时钟的偏移、频差和抖动在线估计,并同步更新了相关状态字段;另外还更新了一个子模块指针。 Changes呈现时间估计模型重构
子模块指针更新
Estimated code review effort: 4 (Complex) | ~45 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@nativelib/src/main/cpp/native_render.cpp`:
- Around line 327-330: The discontinuity check in native_render.cpp only treats
forward PTS jumps greater than 2 seconds as a reset, so smaller reconnect/seek
jumps still flow through the PI path and can skew targetPresentTimeNs. Update
the discontinuity logic in the render timing path around the PTS comparison in
native_render.cpp so that smaller forward jumps within the reconnect/seek range
also trigger a re-anchor based on frame interval or continuity threshold, while
still keeping 2 seconds as the maximum upper bound. Use the existing timing
state such as timeBaseInitialized_ and lastPtsUs_ to detect and reset continuity
before the PI limiter runs.
- Around line 353-357: The PI gain updates in native_render.cpp use bit shifts
on ec, which can bias negative values compared with the intended Kp/Ki behavior.
Update the estimatedOffsetNs_ and skewNs_ calculations in the native render
logic to use division by 64 and 2048 instead of right shifts, keeping the
existing ec clamp and the surrounding PI controller flow intact.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 84afb973-773b-486f-8517-f1a6e0daae1c
📒 Files selected for processing (2)
nativelib/src/main/cpp/native_render.cppnativelib/src/main/cpp/native_render.h
| // 检测 PTS 不连续(重连 / seek / 编码器重启):PTS 回退,或跳变 > 2s。 | ||
| const bool discontinuity = timeBaseInitialized_ && | ||
| (pts < lastPtsUs_ || (pts - lastPtsUs_) > 2000000LL); | ||
|
|
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
补上 2 秒以内的 PTS 正向跳变重锚。
Line 329 只把 >2s 当作不连续;如果 reconnect/seek 让 PTS 正向跳 500ms~2s,会进入 PI 分支并被 ±8ms 限幅,targetPresentTimeNs 仍会偏到未来,可能造成 VSync 冻结。建议按帧间隔设置更近的连续性阈值,保留 2s 作为上限。
🐛 建议修正
- // 检测 PTS 不连续(重连 / seek / 编码器重启):PTS 回退,或跳变 > 2s。
- const bool discontinuity = timeBaseInitialized_ &&
- (pts < lastPtsUs_ || (pts - lastPtsUs_) > 2000000LL);
+ // 检测 PTS 不连续(重连 / seek / 编码器重启):PTS 回退,或明显超过正常帧间隔。
+ const int64_t ptsDeltaUs = pts - lastPtsUs_;
+ int64_t discontinuityThresholdUs = (frameIntervalNs / 1000LL) * 8;
+ if (discontinuityThresholdUs < 250000LL) discontinuityThresholdUs = 250000LL;
+ else if (discontinuityThresholdUs > 2000000LL) discontinuityThresholdUs = 2000000LL;
+ const bool discontinuity = timeBaseInitialized_ &&
+ (ptsDeltaUs < 0 || ptsDeltaUs > discontinuityThresholdUs);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // 检测 PTS 不连续(重连 / seek / 编码器重启):PTS 回退,或跳变 > 2s。 | |
| const bool discontinuity = timeBaseInitialized_ && | |
| (pts < lastPtsUs_ || (pts - lastPtsUs_) > 2000000LL); | |
| // 检测 PTS 不连续(重连 / seek / 编码器重启):PTS 回退,或明显超过正常帧间隔。 | |
| const int64_t ptsDeltaUs = pts - lastPtsUs_; | |
| int64_t discontinuityThresholdUs = (frameIntervalNs / 1000LL) * 8; | |
| if (discontinuityThresholdUs < 250000LL) discontinuityThresholdUs = 250000LL; | |
| else if (discontinuityThresholdUs > 2000000LL) discontinuityThresholdUs = 2000000LL; | |
| const bool discontinuity = timeBaseInitialized_ && | |
| (ptsDeltaUs < 0 || ptsDeltaUs > discontinuityThresholdUs); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@nativelib/src/main/cpp/native_render.cpp` around lines 327 - 330, The
discontinuity check in native_render.cpp only treats forward PTS jumps greater
than 2 seconds as a reset, so smaller reconnect/seek jumps still flow through
the PI path and can skew targetPresentTimeNs. Update the discontinuity logic in
the render timing path around the PTS comparison in native_render.cpp so that
smaller forward jumps within the reconnect/seek range also trigger a re-anchor
based on frame interval or continuity threshold, while still keeping 2 seconds
as the maximum upper bound. Use the existing timing state such as
timeBaseInitialized_ and lastPtsUs_ to detect and reset continuity before the PI
limiter runs.
ec>>6 / ec>>11 为算术右移(向负无穷取整),对负 ec 与预期的 ec/64、 ec/2048(向零取整)差 1 单位。稳态残差常处亚-2048ns 区间,右移每帧 多减 1 并累积进 skew 积分项——离线仿真显示会把 skew 估计带成错误负号。 改为整除后无此直流偏置,且与注释中 Kp=1/64、Ki=1/2048 语义一致。 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
背景
主机(Sunshine)把捕获时刻(QPC 精度)写进 RTP 90kHz 时间戳,common-c 在
RtpVideoQueue.c映射为presentationTimeUs(以首帧为原点的呈现时间)。鸿蒙端native_render一直用它做 host→本地时钟的呈现映射,但旧滤波器是"首帧锚点 + 漂移时硬重置",会周期性产生可见 hitch。改动
CalculatePresentTime升级为 alpha-beta(PI)时钟恢复 + 自适应 cushion:受影响的帧节奏模式
config.enableVsync = true)RenderOutputBufferAtTime,本 PR 改的就是它的呈现时刻计算enableVsync = false)OH_VideoDecoder_RenderOutputBuffer直渲染,完全绕过CalculatePresentTime验证
因 OHOS NDK 全量构建需 DevEco 环境,采用离线仿真(g++,抽取函数原文逐字编译,
-Wall -Wextra零警告)对比新旧算法,4 场景(净 LAN / 典型 WiFi / 抖动远端 / 120fps):风险
Summary by CodeRabbit