WebRTC(九):JitterBuffer

JitterBuffer

Jitter

Jitter”指的是連續到達的媒體包之間時間間隔的變化。在網絡傳輸中,由于:

  • 網絡擁塞
  • 路由路徑變化
  • 隊列排隊
  • 不同鏈路帶寬差異

導致包之間的接收時間不一致,這就是網絡“抖動”。

作用

**JitterBuffer(抖動緩沖區)**的作用是:

  • 緩沖網絡傳輸過來的數據包
  • 重新排序亂序的包
  • 緩沖一定時間再輸出
  • 實現穩定的音視頻幀輸出,避免播放中出現 卡頓、跳幀、音頻破音

工作流程圖

網絡接收 ← UDP/RTP 包 ← jitterbuffer ← 解碼器 ← 播放器/渲染器↑          ↑排序 + 重組 + 時間控制  + 丟包填補(PLC)

流程詳解

1. 接收數據包

  • 每個 RTP 包有 sequence numbertimestamp
  • 收到包后,判斷是否亂序、丟包。

2. 緩存和排序

  • 將包插入 buffer 中合適位置(基于 sequence number 排序)。

3. 播放控制

  • 到達播放時間時,提取對應時間戳的包進行解碼。
  • 若包未到達(丟包或延遲):
    • 等待一段時間(等待時間配置或自適應);
    • 或直接丟幀;
    • 或填補(音頻使用 PLC,視頻可能重復前幀或跳過)。

4. 自適應控制

  • 根據網絡條件(RTCP 報告、丟包率、延遲)動態調整緩沖大小(WebRTC 的核心機制之一)。

常用參數

參數說明
初始緩沖時長(如 50ms)啟動播放前預緩存的時長
最大緩沖時長(如 200ms)抖動緩沖的最大范圍
播放時鐘控制何時從 buffer 中讀包
最大亂序范圍防止惡意/錯誤亂序拖垮 buffer

WebRTC中JitterBuffer

WebRTC 是目前最復雜、最智能的抖動緩沖實現之一,支持:

  • 音頻 JitterBuffer
  • 視頻 JitterBuffer
  • 網絡自適應算法
  • FEC(前向糾錯)/NACK(重傳)
  • Audio/Video 同步

音頻 JitterBuffer

模塊路徑:webrtc/modules/audio_coding/neteq/

功能

  • 亂序處理;
  • 丟包補償(使用 PLC、CNG、FEC);
  • 動態調節;
  • 語音平滑(低碼率時很關鍵);

原理

             RTP Packet↓NetEq::InsertPacket↓[DecoderBuffer + PacketBuffer]↓NetEq::GetAudio (解碼并補償)↓音頻幀 → 播放器

PLC、CNG、FEC

概念
縮寫全稱作用
PLCPacket Loss Concealment在音頻丟包時生成“偽造音頻”以避免突兀中斷
CNGComfort Noise Generation在靜音時生成背景噪聲,防止“死寂”現象
FECForward Error Correction通過多發送冗余信息,在接收端恢復丟失的數據包
PLC(Packet Loss Concealment)

目標:幀丟失時,合成一個與上一幀相似的語音片段,避免“卡頓”或“啞音”。

常用方法

  • 波形復制:簡單地復制上一幀波形;
  • 線性預測(LPC):建模語音信號特性,預測缺失內容;
  • 譜域合成:復制頻譜形狀,適用于寬帶語音(如 Opus);

WebRTC 實現

  • 位于 NetEq 模塊中的 Expand 類;
  • 插入虛擬音頻幀(通常是 10ms);
  • 結合時間戳推進邏輯,自動銜接解碼幀。

NetEq::GetAudio() 會判斷是否缺幀,如缺則調用 Expand::Process() 生成偽音頻。

CNG(Comfort Noise Generation)

目標:通話靜音時生成背景噪聲,增強自然感、避免“真空”感。

常用方法

  • 在“活動語音”段估計背景噪聲特征;
  • 靜音時合成類似背景噪聲(白噪聲加濾波);
  • 由編碼器定期發送 SID(Silence Insertion Descriptor)幀;

WebRTC 實現

  • 使用 RFC 3389 標準 CNG;
  • 位于 NetEq 的 ComfortNoise 模塊;
  • 接收 SID RTP 包并生成偽噪聲;
  • 在編碼器中設置:audio_coding_module->EnableCN(true);
FEC(Forward Error Correction)

目標:通過發送冗余信息,讓接收端自行恢復丟失的幀。

常用方法

  • Opus 內建 FEC:發送低碼率副本;
  • Redundant Encoding (RED):同一個 RTP 包攜帶多個編碼幀;
  • ULPFEC(RFC 5109):按 RTP 層進行異或編碼恢復丟包;

WebRTC 實現

  • 支持 Opus FEC(內建);

  • 支持 RED + ULPFEC 組合(多用于視頻,但音頻也適用);

  • 啟用方式:

    config.audio.send_codec_spec.codec_inst.pltype = 111; // opus
    config.audio.send_codec_spec.enable_fec = true;
    

Opus 中 FEC 和 DTX 可協同工作(低帶寬時啟用 DTX 靜音,失真時啟用 FEC)

對比
技術工作階段需編碼器支持占帶寬延遲對音質的作用
PLC接收端平滑丟包間斷
CNG編碼 + 解碼極低模擬背景環境
FEC編碼 + 解碼主動對抗丟包,避免掉幀
WebRTC 中啟用方式
啟用 PLC(默認開啟)

無需顯式設置,NetEq 自動啟用:

NetEq::GetAudio() 自動判斷是否丟包 → Expand::Process()
啟用 CNG
AudioSendStream::Config config;
config.send_codec_spec.codec_inst.pltype = 9; // G.729 CN
config.send_codec_spec.enable_dtx = true;     // 打開 DTX

對于 Opus,也可以開啟 DTX(自動靜音 + CNG):

config.send_codec_spec.enable_dtx = true;
啟用 FEC(以 Opus 為例)
config.send_codec_spec.enable_fec = true;

也可通過 SDP 啟用 RED + ULPFEC:

a=rtpmap:111 opus/48000/2
a=fmtp:111 useinbandfec=1; usedtx=1

NetEq

功能

功能說明
抖動緩沖緩解網絡抖動帶來的亂序、延遲不穩定
解碼插件式音頻解碼器支持
丟包補償(PLC)使用語音擴展、靜音插入等技術“補”上丟幀
噪聲生成(CNG)模擬背景噪聲防止靜音突兀
拓展播放/速率控制實現播放速度調節(例如加速恢復)
DTMF 支持電話撥號音的內聯處理
關鍵類:NetEqImpl

核心類是:

class NetEqImpl : public NetEq {public:int InsertPacket(const RTPHeader& header, rtc::ArrayView<const uint8_t> payload) override;int GetAudio(AudioFrame* audio_frame) override;...
};
InsertPacket()
int NetEqImpl::InsertPacket(const RTPHeader& header,rtc::ArrayView<const uint8_t> payload)

處理 RTP 包輸入:

  • 插入 packet_buffer_
  • 檢查有效性、亂序
  • 更新時間戳信息
GetAudio()
int NetEqImpl::GetAudio(AudioFrame* audio_frame)

執行一次音頻播放輸出:

  • 調用 decision_logic_->GetDecision() 選擇行為
  • 行為包括:
    • kNormal:正常解碼
    • kExpand:PLC 補償
    • kAccelerate:播放加速
    • kCng:背景噪聲
  • 執行相應模塊生成音頻幀返回
運行機制:時間推進和緩沖策略

NetEq 使用內部“播放時鐘”推進播放,假設 10ms 一幀,每次 GetAudio() 會:

  1. 計算目標 timestamp
  2. 判斷當前 packet buffer 是否含有該 timestamp 的幀
  3. 沒有 → 觸發補償
  4. 有 → 解碼返回
源碼解析
PacketBuffer

存儲 RTP 包,支持按 timestamp 排序 + 亂序插入:

class PacketBuffer {bool InsertPacket(Packet&& packet);absl::optional<Packet> GetNextPacket(uint32_t timestamp);
};
DecoderDatabase

注冊各種 RTP payload type 到解碼器:

class DecoderDatabase {bool RegisterPayload(uint8_t payload_type, AudioDecoder* decoder);AudioDecoder* GetDecoder(uint8_t payload_type);
};

可擴展添加自定義解碼器。

Expand(PLC)

用于在丟包時合成連續音頻:

class Expand {void Process(AudioFrame* frame);
};

算法核心:基于最近解碼幀的頻率模式生成偽數據。

視頻 JitterBuffer

模塊路徑:webrtc/modules/video_coding/

功能

  • 基于幀(Frame)級緩存;
  • 管理多個 RTP 包拼裝一個視頻幀;
  • 處理 I/P/B 幀依賴關系;
  • 異步解碼與播放,配合 AVSync。

核心類:

  • VCMJitterBuffer:包級緩存;
  • FrameBuffer:幀組裝器;
  • FrameBufferController:根據解碼狀態/網絡反饋動態調節 buffer;

原理

1. DeliverRtp(RTP packet)↓
2. Insert into FrameBuffer (reorders and assembles)↓
3. Mark frame as complete↓
4. Notify decoder thread (via AsyncInvoker)↓
5. Decoder calls NextFrame()↓
6. FrameBuffer returns suitable frame based on timing

源碼解析

FrameBuffer 接口類
class FrameBuffer {public:void InsertFrame(std::unique_ptr<EncodedFrame> frame);std::unique_ptr<EncodedFrame> NextFrame();
};

特點:

  • 接收完整幀(非 RTP 包級);
  • 和 NACK 控制、幀到達策略分離;
  • 提供解碼時間控制(配合 Timing 類);
RtpVideoStreamReceiver

接收 RTP 包并重組幀,組裝完成后推入 FrameBuffer

bool RtpVideoStreamReceiver::OnRtpPacket(const RtpPacketReceived& packet)
  • 組裝 VCMPacket(含 marker bit, seq, timestamp);
  • 查找是否構成完整幀(依賴 FrameBuffer::InsertFrame());
  • 完整幀則通知解碼線程處理。
VideoReceiveStream::StartDecodeLoop()

負責調用解碼邏輯:

std::unique_ptr<EncodedFrame> frame = frame_buffer_->NextFrame();
decoder_->Decode(frame);

解碼線程會持續等待并從 FrameBuffer 中提取適合解碼的幀。

時間同步邏輯(配合 Timing 類)

視頻幀不是立刻解碼,而是要等待“最佳播放時間”:

Timing::RenderTimeMs(uint32_t frame_timestamp, int64_t now_ms)

內部通過系統時間、RTP timestamp 差計算出:

  • 當前幀是否提前(buffering)
  • 當前幀是否延遲(丟幀)
  • 幀間 jitter 均值估計(變更播放時鐘)
丟包處理(NACK / Frame Missing)
  1. FrameBuffer::InsertFrame() 內部跟蹤丟幀(依據 sequence number);
  2. 控制模塊向上層觸發 NACK;
  3. 使用 rtp_rtcp::RTCPeerFeedback 上報丟幀;
  4. 等待 retransmit 后再組幀。

動態自適應機制

WebRTC 會根據網絡反饋(RTCP)動態調整 jitterbuffer:

網絡狀態Buffer 調整策略
抖動變大增大 buffer 延遲,提升穩定性
網絡穩定減小 buffer,降低延遲
丟包嚴重增加 buffer + 請求重傳(NACK)
無法重傳使用 FEC 或插入靜音/偽幀

與 AV 同步的協作

WebRTC 中,音頻是時鐘主導(anchor),視頻 jitterbuffer 會與音頻同步,控制渲染時間戳,使音畫同步。

總結

WebRTC 的 JitterBuffer 構建了高度模塊化、可插拔、跨平臺的實時緩沖機制,實現了在復雜網絡環境下高質量的音視頻通信體驗。

特性音頻(NetEq)視頻(FrameBuffer)
緩沖粒度RTP 包(10ms)視頻幀
解碼策略嚴格 10ms 推進根據時間和幀依賴
丟包處理PLC / CNGNACK / 丟棄
時間同步插值輸出 / 靜音填充Timing::RenderTimeMs 控制
解碼控制內部自動控制外部線程主動拉幀解碼
延遲適配加速 / 減速控制解碼時機或丟幀

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/86703.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/86703.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/86703.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【推薦100個unity插件】在 Unity 中繪制 3D 常春藤,模擬生長——hedera插件的使用

注意&#xff1a;考慮到后續接觸的插件會越來越多&#xff0c;我將插件相關的內容單獨分開&#xff0c;并全部整合放在【推薦100個unity插件】專欄里&#xff0c;感興趣的小伙伴可以前往逐一查看學習。 效果演示 文章目錄 效果演示前言一、常春藤生成器工具下載二、工具使用1、…

【三維重建】【3DGS系列】【深度學習】3DGS的理論基礎知識之高斯橢球的幾何變換

【三維重建】【3DGS系列】【深度學習】3DGS的理論基礎知識之高斯橢球的幾何變換 文章目錄 【三維重建】【3DGS系列】【深度學習】3DGS的理論基礎知識之高斯橢球的幾何變換前言模型變換(Model Transformation)觀測變換(Viewing Transformation)視圖變換(View Transformation)投影…

EXISTS 和 NOT EXISTS 、IN (和 NOT IN)

在 SQL 中&#xff0c;EXISTS、NOT EXISTS 和 IN 都是用于子查詢的條件運算符&#xff0c;用于根據子查詢的結果過濾主查詢的行。它們之間的區別主要體現在工作方式、效率、對 NULL 值的處理以及適用場景上。 1. EXISTS 和 NOT EXISTS 作用&#xff1a; EXISTS: 檢查子查詢是…

GitHub 趨勢日報 (2025年06月25日)

&#x1f4ca; 由 TrendForge 系統生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日報中的項目描述已自動翻譯為中文 &#x1f4c8; 今日獲星趨勢圖 今日獲星趨勢圖 880 awesome 788 build-your-own-x 691 free-for-dev 427 best-of-ml-python 404 …

互聯網大廠Java求職面試:Java虛擬線程實戰

互聯網大廠Java求職面試&#xff1a;Java虛擬線程實戰 文章內容 開篇&#xff1a;技術總監與程序員鄭薪苦的三輪對話 在一場緊張而嚴肅的Java工程師面試中&#xff0c;技術總監張工正對候選人鄭薪苦進行深入提問。鄭薪苦雖然性格幽默&#xff0c;但對技術有著扎實的理解。今天…

網絡安全的兩大威脅:XSS與CSRF攻擊實例解析

在網絡攻擊中,XSS跨站腳本攻擊(Cross Site Scripting)與CSRF跨站請求偽造攻擊(Cross-Site Request Forgery)是兩種常見的攻擊方式,它們之間存在顯著的區別。以下是對這兩種攻擊方式的詳細比較: 一、攻擊原理 XSS跨站腳本攻擊 攻擊者通過在Web頁面中注入惡意腳本來實現攻…

如何一次性將 iPhone 中的聯系人轉移到 PC

許多重要的聯系人都存儲在您的 iPhone 上。為了保護關鍵信息&#xff0c;您可能需要將聯系人從 iPhone 轉移到 PC&#xff0c;這是一種有效的聯系人備份方法。如果您在將 iPhone 聯系人轉移到電腦上遇到困難&#xff0c;現在可以從本文中學習 5 個有效的解決方案&#xff0c;然…

Spring Boot開啟定時任務的三種方式 【@EnableScheduling注解,SchedulingConfigurer接口,Quartz 框架】

Spring Boot 開啟定時任務的三種方式? ? ? 在 Spring Boot 應用開發過程中&#xff0c;定時任務是十分常見的需求&#xff0c;比如定時清理日志文件、定期備份數據庫數據、定時發送郵件提醒等。Spring Boot 提供了多種開啟定時任務的方式&#xff0c;本文將詳細介紹三種常見…

LLM 編碼器 怎么實現語義相關的 Token 向量更貼近? mask訓練:上下文存在 ;; 自回歸訓練:只有上文,生成模型

LLM 編碼器 怎么實現語義相關的 Token 向量更貼近? 目錄 LLM 編碼器 怎么實現語義相關的 Token 向量更貼近?mask訓練:上下文存在自回歸訓練:只有上文,生成模型一、核心機制:損失函數與反向傳播的“語義校準”1. 損失函數的“語義約束”2. 嵌入層參數的“動態調整”二、關…

從OCR瓶頸到結構化理解來有效提升RAG的效果

當人們探討如何讓人工智能系統更好地從文檔中查找和使用信息時&#xff0c;通常關注的是令人矚目的算法和前沿的大型語言模型。但問題是&#xff1a;如果文本提取的質量很差&#xff0c;那么后續的努力都將付諸東流。本文探討OCR質量如何影響檢索增強生成&#xff08;RAG&#…

SpringBoot -- 整合Junit

11.SpringBoot 整合 Junit 11.1 為什么需要單元測試 由于在SpringBoot開發過程中&#xff0c;每開發一個模塊&#xff0c;有時需要從 controller、service、mapper 到甚至 xml 文件的編寫全部開發完畢才能進行測試&#xff0c;這是十分浪費時間的&#xff0c;比如開發人員想測…

虛擬機遠程連接編譯部署QT程序

概要 邏輯 我們需要湊齊 QT庫、交叉編譯工具、sysroot這三大件。 交叉編譯的程序是部署到板卡環境運行,需要構建和板卡一樣的庫環境。 sysroot是我們在虛擬機上自己命名的一個文件夾,包含開發板的運行系統所需的所有文件。 虛擬機是x64版本,開發板是arm64版本。 如果開發板…

基于SpringBoot的智慧旅游系統

以智慧旅游系統的設計與實現為研究對象&#xff0c;旨在通過科技手段提升旅游業的管理效能和游客體驗。在系統設計方面&#xff0c;深入分析了地理特征、豐富的文化底蘊以及多樣的自然景觀。結合這些獨特之處&#xff0c;構建了一個多層次的旅游管理系統&#xff0c;包括景點信…

下載最新版本的OpenOCD

Download OpenOCD for Windowsd&#xff1a; https://gnutoolchains.com/arm-eabi/openocd/

Geollama 輔助筆記:raw_to_prompt_strings_geo.py

1 GeoLifePreprocessingDF 1.1 創造函數 1.2 讀取原始數據 讀取這個DataFrame 1.3 處理原始DataFrame 1.4 生成對應prompt 1.5 打亂軌跡 1.6 打亂軌跡里面的事件

TDengine 如何打破工業實時數據庫勢力邊界?

打破工業實時數據庫勢力邊界&#xff0c;TDengine 時序數據庫與工業 SCADA 深度融合 隨著 時序數據庫&#xff08;Time Series Database&#xff09;的日益普及&#xff0c;越來越多的工業自動化控制&#xff08;工控&#xff09;人員開始認識到其強大能力。然而&#xff0c;時…

滲透靶場:事件和屬性被阻止的反射xss

本關很多標簽被攔截了&#xff0c;需要使用 burp 模糊測試哪個標簽可以用 <a>和<animate>可以使用&#xff0c;<animate>是<svg>標簽中用來給動畫設定屬性的&#xff0c;看看<svg>可不可用 利用<svg>、<animate>、<a>來構造 這…

STM32中Usart的使用

目錄 一、USART簡介 1.電平標準 2.通信接口 3.硬件電路 4.串口參數以及時序 5.串口時序 二、USART結構介紹 1.USART功能框圖 ?編輯 1.1 功能引腳 1.2 數據寄存器 1.3 控制器 1.4 波特率發生器 1.5簡化結構圖 2.數據幀 一、USART簡介 USART&#xff08;Universa…

鴻蒙HarmonyOS 5小游戲實踐:數字記憶挑戰(附:源代碼)

數字記憶挑戰游戲&#xff1a;打造提升大腦記憶力的鴻蒙應用 在當今數字時代&#xff0c;人們的記憶力面臨著前所未有的挑戰。從日常的待辦事項到復雜的工作任務&#xff0c;強大的記憶力都是提高效率和表現的關鍵。本文將介紹一款基于鴻蒙系統開發的數字記憶挑戰游戲&#xf…

記錄一個C#/.NET的HTTP工具類

記錄一個C#/.NET的HTTP工具類 using Serilog; using System.Net; using System.Text; using System.Text.Json;namespace UProbe.Common.Comm.Http {public class HttpClientHelper{/// <summary>/// 發送HttpGet請求/// </summary>/// <typeparam name"T…